Advanced Lane Finding Project

The goal of the project to develop image processing pipeline to be able to identify lane lines under various lighting condition, apply thresholding and masking, fit second-order polynomial and measure the curvature of the road.

The high-level overview of the project:

  1. Camera calibration and distortion correction
  2. Processing road images
    1. Colorspace and Sobel thresholding
    2. Perspective transform
    3. Detecting lane lines and fitting polynomials
    4. Measuring Curvature and vehicle offset
    5. Putting all the steps together
    6. Visualizing results of image pipeline
  3. Object oriented design
  4. Video processing pipeline

1. Camera calibration and distortion correction

1.1 Find chessboard corners

First, we use OpenCV to findChessboardCorners and identify corresponding objpoints (ideal coordinates of a chessboard plane in a 3D space, e.g. [2., 0., 0.], z-coordinate always 0) and imgpoints (real 2D-pixel coordinates of chessboard corners on a photo, e.g. [265.0, 631.7]). We then use calibrateCamera to find camera matrix and distortion coefficients.

In [1]:
import os
import re
import sys
import cv2
import glob
import numpy as np
import datetime
import argparse
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import pickle
import math

# Visualizations will be shown in the notebook.
%matplotlib inline

After having done our imports, we read image names using glob API and prepare ideal coordinates of nx by ny chessboard:

In [2]:
#calibration images
images_cal = glob.glob("camera_cal/calibration*")
#size of the chessboard
nx = 9
ny = 6
#analytically find chessboard coordinates: from [0,0,0] to [nx,ny,0]
objp = np.zeros((nx*ny,3), np.float32)
objp[:,:2] = np.mgrid[0:nx, 0:ny].T.reshape(-1,2)
# Lists to store object points and image points from all calibration images.
objpoints = [] # 3d analytical points in real world space
imgpoints = [] # 2d real points in image plane.

For convenience, we define natural sorting, opposite to lexographical sorting, see sorting order below:

In [3]:
#natural sorting: sort calibration images with natural keys, not lexographically
def atoi(text):
    return int(text) if text.isdigit() else text

def natural_keys(text):
    '''
    alist.sort(key=natural_keys) sorts in human order
    http://nedbatchelder.com/blog/200712/human_sorting.html
    '''
    return [ atoi(c) for c in re.split('(\d+)', text) ]

images_cal.sort(key=natural_keys)

for img_fname in images_cal:
    print(img_fname)
camera_cal/calibration1.jpg
camera_cal/calibration2.jpg
camera_cal/calibration3.jpg
camera_cal/calibration4.jpg
camera_cal/calibration5.jpg
camera_cal/calibration6.jpg
camera_cal/calibration7.jpg
camera_cal/calibration8.jpg
camera_cal/calibration9.jpg
camera_cal/calibration10.jpg
camera_cal/calibration11.jpg
camera_cal/calibration12.jpg
camera_cal/calibration13.jpg
camera_cal/calibration14.jpg
camera_cal/calibration15.jpg
camera_cal/calibration16.jpg
camera_cal/calibration17.jpg
camera_cal/calibration18.jpg
camera_cal/calibration19.jpg
camera_cal/calibration20.jpg

We now visualize the original chessboard images:

In [4]:
NUM_COLUMNS = 4
NUM_CAL_IMG = len(images_cal)
NUM_ROWS = math.ceil(NUM_CAL_IMG / NUM_COLUMNS)
fig = plt.figure(figsize=(20,16))
for idx, fname in enumerate(images_cal):
    img = mpimg.imread(fname)
    ax = fig.add_subplot(NUM_ROWS,NUM_COLUMNS,idx+1)
    ax.imshow(img, interpolation='none')
    ax.set_title(fname,  fontsize=14)
    ax.set_xticks([])
    ax.set_yticks([])
plt.tight_layout(w_pad=-0.3, h_pad=-0.4)
plt.suptitle('Calibration Images', y=1.02, fontsize=30)
plt.show()

As can be seen in the textual output and the picture below, chessboard corners are found not in every image. If not found, we display a black square.

In [5]:
#finding & displaying chessboard corners
NUM_COLUMNS = 4
NUM_CAL_IMG = len(images_cal)
NUM_ROWS = math.ceil(NUM_CAL_IMG / NUM_COLUMNS)
fig = plt.figure(figsize=(20,16))

for idx, fname in enumerate(images_cal):
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # Find the chessboard corners
    ret, corners = cv2.findChessboardCorners(gray, (nx,ny), None)
    ax = fig.add_subplot(NUM_ROWS,NUM_COLUMNS,idx+1)
    ax.set_title(fname,  fontsize=14)
    ax.set_xticks([])
    ax.set_yticks([])
    # If found, add object points, image points
    if ret == True:
        print("Chessboard found for {}".format(fname))
        objpoints.append(objp)
        imgpoints.append(corners)
        #Draw the chessboard corners
        cv2.drawChessboardCorners(img, (nx,ny), corners, ret)
        ax.imshow(img, interpolation='none')
    else:
        ax.imshow(np.zeros_like(img))
plt.tight_layout(w_pad=-0.3, h_pad=-0.4)
plt.suptitle('Chessboard Corners', y=1.02, fontsize=30)
plt.show()
Chessboard found for camera_cal/calibration2.jpg
Chessboard found for camera_cal/calibration3.jpg
Chessboard found for camera_cal/calibration6.jpg
Chessboard found for camera_cal/calibration7.jpg
Chessboard found for camera_cal/calibration8.jpg
Chessboard found for camera_cal/calibration9.jpg
Chessboard found for camera_cal/calibration10.jpg
Chessboard found for camera_cal/calibration11.jpg
Chessboard found for camera_cal/calibration12.jpg
Chessboard found for camera_cal/calibration13.jpg
Chessboard found for camera_cal/calibration14.jpg
Chessboard found for camera_cal/calibration15.jpg
Chessboard found for camera_cal/calibration16.jpg
Chessboard found for camera_cal/calibration17.jpg
Chessboard found for camera_cal/calibration18.jpg
Chessboard found for camera_cal/calibration19.jpg
Chessboard found for camera_cal/calibration20.jpg

1.2 Undistort calibration images

We now find camera matrix and distortion coefficients and undistort camera calibration images.

In [6]:
img = mpimg.imread(images_cal[0])
img_size = (img.shape[1], img.shape[0])

# Do camera calibration given object points and image points
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, img_size,None,None)

if ret:
    print("Camera was calibrated successfully!")
else:
    print("Camera calibration failed!")
Camera was calibrated successfully!

Visualize undistorted calibration images:

In [7]:
#displaying undistorted chessboard images
NUM_COLUMNS = 4
NUM_CAL_IMG = len(images_cal)
NUM_ROWS = math.ceil(NUM_CAL_IMG / NUM_COLUMNS)
fig = plt.figure(figsize=(20,16))

for idx, fname in enumerate(images_cal):
    img = cv2.imread(fname)
    dst = cv2.undistort(img, mtx, dist, None, mtx)
    ax = fig.add_subplot(NUM_ROWS,NUM_COLUMNS,idx+1)
    ax.set_title(fname,  fontsize=14)
    ax.set_xticks([])
    ax.set_yticks([])
    # If found, add object points, image points
    ax.imshow(dst, interpolation='none')
plt.tight_layout(w_pad=-0.3, h_pad=-0.4)
plt.suptitle('Undistorted chessboard images', y=1.02, fontsize=30)
plt.show()

1.3 Save camera matrix and distortion coefficients

We now save the camera matrix and distortion coefficients to the pickle file (for the future, we also save rotation rvecs and translation tvecs vectors).

In [8]:
camera_pickle = {}
camera_pickle["mtx"] = mtx
camera_pickle["dist"] = dist
camera_pickle["rvecs"] = rvecs
camera_pickle["tvecs"] = tvecs
with open("camera_pickle.p", "wb") as p:
    pickle.dump( camera_pickle, p )

Camera class is responsible for holding camera matrix and distortion coefficients and undistorting images from a camera.

In [9]:
class Camera:
    
    def __init__(self, pickle_file = 'camera_pickle.p'):
        self.pickle = pickle_file
        with(open(self.pickle, 'rb')) as p:
            camera_pickle = pickle.load(p)
            self.mtx = camera_pickle["mtx"]
            self.dist = camera_pickle["dist"]
    
    def undistort(self, img):
        return cv2.undistort(img, self.mtx, self.dist, None, self.mtx)
In [10]:
camera = Camera()

In this section we will incrementally build the image processing pipeline. We start from applying variants of Sobel operator (in x and y direction, magnitude (L2-norm), and direction (arctan(y/x)). We then transform an image to the HLS colorspace and apply the thresholds there. We then combine Sobel and color masks to get our binary mask. In the next step we use perspective transform to obtain "bird's-eye-view" of the road.

In [11]:
images_road = glob.glob("test_images/*")
images_road.sort(key=natural_keys)
for img in images_road:
    print(img)
test_images/straight_lines1.jpg
test_images/straight_lines2.jpg
test_images/test1.jpg
test_images/test2.jpg
test_images/test3.jpg
test_images/test4.jpg
test_images/test5.jpg
test_images/test6.jpg
In [12]:
NUM_COLUMNS = 4
NUM_ROAD_IMG = len(images_road)
NUM_ROWS = math.ceil(NUM_ROAD_IMG / NUM_COLUMNS)
fig = plt.figure(figsize=(20,7))
for idx, fname in enumerate(images_road):
    img = mpimg.imread(fname)
    dst = camera.undistort(img)
    ax = fig.add_subplot(NUM_ROWS,NUM_COLUMNS,idx+1)
    ax.imshow(dst, interpolation='none')
    ax.set_title(fname,  fontsize=14)
    ax.set_xticks([])
    ax.set_yticks([])
plt.tight_layout(w_pad=-0.3, h_pad=-1.0)
plt.suptitle('Undistorted test images of the road', y=1.02, fontsize=30)
plt.show()

2.1. Colorspace and Sobel thresholding

Given various colors of the lane lines, various lightning condition, we apply Sobel and colorspace transform and thresholding to identify lane lines on different roads. We first compute (i) absolute value of Sobel filter in x and y direction; (ii) gradient magnitude of the Sobel filter (l2-norm); (iii) directional threshold where we take arctan y / x for directional Sobel filters. We then combine all these thresholds in threshold_image method. We also use HLS colorspace and find a color threshold in hls_threshold. Color thresholds and Sobel thresholds are combined in the apply_thresholds method, which takes an undistorted image and returnes a thresholded binary image.

In [13]:
def abs_sobel_thresh(image, orient='x', sobel_kernel=3, thresh=(0, 255)):
    # Calculate directional gradient: x or y 
    # Apply threshold
    gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
    # Apply x or y gradient with the OpenCV Sobel() function
    # and take the absolute value
    if orient == 'x':
        abs_sobel = np.absolute(cv2.Sobel(gray, cv2.CV_64F, 1, 0))
    if orient == 'y':
        abs_sobel = np.absolute(cv2.Sobel(gray, cv2.CV_64F, 0, 1))
    # Rescale back to 8 bit integer
    scaled_sobel = np.uint8(255*abs_sobel/np.max(abs_sobel))
    # Create a copy and apply the threshold
    grad_binary = np.zeros_like(scaled_sobel)
    grad_binary[(scaled_sobel >= thresh[0]) & (scaled_sobel <= thresh[1])] = 1
    return grad_binary

def mag_thresh(image, sobel_kernel=3, mag_thresh=(0, 255)):
    # Calculate gradient magnitude (l2-norm)
    # Convert to grayscale
    gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
    # Take both Sobel x and y gradients
    sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=sobel_kernel)
    sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=sobel_kernel)
    # Calculate the gradient magnitude
    gradmag = np.sqrt(sobelx**2 + sobely**2)
    # Rescale to 8 bit
    scale_factor = np.max(gradmag)/255 
    gradmag = (gradmag/scale_factor).astype(np.uint8) 
    # Create a binary image of ones where threshold is met, zeros otherwise
    mag_binary = np.zeros_like(gradmag)
    mag_binary[(gradmag >= mag_thresh[0]) & (gradmag <= mag_thresh[1])] = 1
    return mag_binary

def dir_threshold(image, sobel_kernel=3, thresh=(0, np.pi/2)):
    # Calculate gradient direction -- arctan(y/x)
    # Apply threshold
    # 1) Convert to grayscale
    gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
    # 2) Take the gradient in x and y separately
    sobel_x = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize = sobel_kernel)
    sobel_y = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize = sobel_kernel)
    # 3) Take the absolute value of the x and y gradients
    abs_sobel_x = np.absolute(sobel_x)
    abs_sobel_y = np.absolute(sobel_y)
    # 4) Use np.arctan2(abs_sobely, abs_sobelx) to calculate the direction of the gradient 
    grad_dir = np.arctan2(abs_sobel_y, abs_sobel_x)
    # 5) Create a binary mask where direction thresholds are met
    dir_binary = np.zeros_like(grad_dir)
    # 6) Return this mask as your binary_output image
    dir_binary[(grad_dir >= thresh[0]) & (grad_dir <= thresh[1])] = 1
    return dir_binary

def threshold_image(image, x_kernel=3, x_thresh=(30, 110), y_kernel=3, y_thresh=(40,110),
                           m_kernel=3, m_thresh=(30, 80),  d_kernel=7, d_thresh=(0.8,1.3)):
    gradx = abs_sobel_thresh(image, orient='x', sobel_kernel=x_kernel, thresh=x_thresh)
    grady = abs_sobel_thresh(image, orient='y', sobel_kernel=y_kernel, thresh=y_thresh)
    mag_binary = mag_thresh(image, sobel_kernel=m_kernel, mag_thresh=m_thresh)
    dir_binary = dir_threshold(image, sobel_kernel=d_kernel, thresh=d_thresh)
    
    combined = np.zeros_like(dir_binary)
    #combined[((gradx == 1) & (grady == 1)) | ((mag_binary == 1) & (dir_binary == 1))] = 1
    combined[((gradx == 1)) | ((mag_binary == 1)) & (dir_binary == 1)] = 1

    return combined

def hls_threshold(img, s_thresh=(130, 255), l_thresh=(90,255)):
    hls = cv2.cvtColor(img, cv2.COLOR_RGB2HLS)
    l_channel = hls[:,:,1]
    s_channel = hls[:,:,2]
    s_binary = np.zeros_like(s_channel)
    s_binary[(s_channel >= s_thresh[0]) & (s_channel <= s_thresh[1])] = 1
    l_binary = np.zeros_like(l_channel)
    l_binary[(l_channel >= l_thresh[0]) & (l_channel <= l_thresh[1])] = 1
    return s_binary, l_binary

def apply_thresholds(img):
    s_binary, l_binary = hls_threshold(img)
    out_img = threshold_image(img)
    combined_binary = np.zeros_like(out_img)
    combined_binary[ (l_binary == 1) & (s_binary == 1) | (out_img == 1)] = 1
    return combined_binary

def get_color_binary(img):
    s_binary, l_binary = hls_threshold(img)
    # Stack each channel to view their individual contributions in green and blue respectively
    # This returns a stack of the two binary images, whose components you can see as different colors
    color_binary = np.uint8(np.dstack(( np.zeros_like(s_binary), l_binary, s_binary)) * 255)
    return color_binary

Current version of the image pipeline looks as follows: (i) undistort an image; (ii) apply thresholding

In [14]:
def image_pipiline_V_0_1(img):
    undist = camera.undistort(img)
    binary_thresh = apply_thresholds(undist)
    return binary_thresh

Let's visualize results of thresholding: Sobel thresholding, colorspace thresholding, combined thresholding:

In [15]:
# Plotting Sobel, colorspace and combined thesholds
NUM_COLUMNS = 3
NUM_ROAD_IMG = 3*len(images_road)
NUM_ROWS = math.ceil(NUM_ROAD_IMG / NUM_COLUMNS)
fig = plt.figure(figsize=(20,30))
for idx, fname in enumerate(images_road):
    img = mpimg.imread(fname)
    undist = camera.undistort(img)
    sobel_img = threshold_image(undist)
    color_binary = get_color_binary(undist)
    combined_binary = apply_thresholds(undist)
    ax = fig.add_subplot(NUM_ROWS,NUM_COLUMNS,3*idx+1)
    ax.imshow(sobel_img, interpolation='none', cmap='gray')
    ax.set_title('Binary thresholds: Sobel, Magnitute, Direction',  fontsize=14)
    ax.set_xticks([])
    ax.set_yticks([])
    ax = fig.add_subplot(NUM_ROWS,NUM_COLUMNS,3*idx+2)
    ax.imshow(color_binary, interpolation='none')
    ax.set_title('Stacked S and L colorspace thresholds',  fontsize=14)
    ax.set_xticks([])
    ax.set_yticks([])
    ax = fig.add_subplot(NUM_ROWS,NUM_COLUMNS,3*idx+3)
    ax.imshow(combined_binary, interpolation='none', cmap='gray')
    ax.set_title('Combined color thresholds with Sobel',  fontsize=14)
    ax.set_xticks([])
    ax.set_yticks([])
plt.tight_layout(w_pad=-0.3, h_pad=-0.2)
plt.suptitle('Applying thresholding to the road images', y=1.02, fontsize=30)
plt.show()

2.2. Perspective transform

The next step is to perform perspective transformation, and obtain the so-called "bird's-eye-view" of the road. We identify source and destinations polyhedra for image transformation. We use straight line image straight_lines1.jpg to find a polyhedron and transform it to rectangular. Method vertices_img_pipeline returns source and destination vertices. Method region_of_interest (from the first project) zeros out the pixels that are outside of the road segment.

In [16]:
def vertices_img_pipeline(img):
    """Define source and destination vertices for perspective transform
    """
    w = img.shape[1]
    h = img.shape[0]
    d_w = w/25
    d_h = 95
    bottom_w_offset = 425
    bottom_h_offset = 15
    w_offset = 0
    src_v1 = [w/2 + w_offset - bottom_w_offset, h - bottom_h_offset]
    src_v2 = [w/2 + w_offset - d_w, h/2 + d_h ]
    src_v3 = [w/2 + w_offset + d_w, h/2 + d_h ]
    src_v4 = [w/2 + w_offset + bottom_w_offset, h - bottom_h_offset]
    src_vertices = np.array( [[src_v1, src_v2, src_v3, src_v4]], dtype=np.float32 )
    dst_offset = 110
    dst_v1 = [src_vertices[0,0,0] + dst_offset, 720]
    dst_v2 = [src_vertices[0,0,0] + dst_offset, 0]
    dst_v3 = [src_vertices[0,3,0] - dst_offset, 0]
    dst_v4 = [src_vertices[0,3,0] - dst_offset, 720]
    dst_vertices = np.array( [[dst_v1, dst_v2, dst_v3, dst_v4]], dtype=np.float32 )
    return src_vertices, dst_vertices

def region_of_interest(img):
    """
    Applies an image mask.
    
    Only keeps the region of the image defined by the polygon
    formed from `vertices`. The rest of the image is set to black.
    """
    src_vertices, _ = vertices_img_pipeline(img)

    src_v1 = src_vertices[0,0]
    src_v2 = src_vertices[0,1]
    src_v3 = src_vertices[0,2]
    src_v4 = src_vertices[0,3]
    mask_w_bottom_offset = 100
    mask_w_top_offset = 5
    mask_h_top_offset = 10
    mask_v1 = [src_v1[0] - mask_w_bottom_offset, src_v1[1]]
    mask_v2 = [src_v2[0] - 5, src_v2[1] - mask_h_top_offset]
    mask_v3 = [src_v3[0] + 30, src_v3[1] - mask_h_top_offset]
    mask_v4 = [src_v4[0] + mask_w_bottom_offset, src_v4[1]]
    vertices_mask = np.array([[mask_v1, mask_v2, mask_v3, mask_v4]], dtype=np.int32)
    
    #defining a blank mask to start with
    mask = np.zeros_like(img)   
    
    #defining a 3 channel or 1 channel color to fill the mask with depending on the input image
    if len(img.shape) > 2:
        channel_count = img.shape[2]  # i.e. 3 or 4 depending on your image
        ignore_mask_color = (255,) * channel_count
    else:
        ignore_mask_color = 255
        
    #filling pixels inside the polygon defined by "vertices" with the fill color    
    cv2.fillPoly(mask, vertices_mask, ignore_mask_color)
    
    #returning the image only where mask pixels are nonzero
    masked_image = cv2.bitwise_and(img, mask)
    return masked_image

def change_perspective(img, inv=False):
    src, dst = vertices_img_pipeline(img)
    if not inv:
        M = cv2.getPerspectiveTransform(src, dst)
    else:
        M = cv2.getPerspectiveTransform(dst, src)
    img_size = (img.shape[1], img.shape[0])
    warped = cv2.warpPerspective(img, M, img_size, flags=cv2.INTER_LINEAR)
    return warped

def image_pipiline_V_0_2(img):
    undist = camera.undistort(img)
    binary_thresh = apply_thresholds(undist)
    binary_thresh = np.uint8(binary_thresh)
    binary_masked = region_of_interest(binary_thresh)
    warped = change_perspective(binary_masked)
    return warped

Visualization of perspective transform:

In [17]:
# Plotting original, warped, and masked images
NUM_COLUMNS = 3
NUM_ROAD_IMG = 3*len(images_road)
NUM_ROWS = math.ceil(NUM_ROAD_IMG / NUM_COLUMNS)
fig = plt.figure(figsize=(20,30))
for idx, fname in enumerate(images_road):
    img = mpimg.imread(fname)
    undist = camera.undistort(img)
    src, dst = vertices_img_pipeline(undist)
    combined_binary = apply_thresholds(undist)
    warped_orig = change_perspective(undist)
    warped_mask = region_of_interest(combined_binary)
    warped_mask = change_perspective(warped_mask)
    cv2.polylines(undist,np.int32(src),True,(0,255,255),3)
    cv2.polylines(warped_orig,np.int32(dst),True,(0,255,255),3)
    cv2.polylines(warped_mask,np.int32(dst),True,(0,255,255),4)
    ax = fig.add_subplot(NUM_ROWS,NUM_COLUMNS,3*idx+1)
    ax.imshow(undist, interpolation='none')
    ax.set_title('Defining polyhedron for perspective transform',  fontsize=14)
    ax.set_xticks([])
    ax.set_yticks([])
    ax = fig.add_subplot(NUM_ROWS,NUM_COLUMNS,3*idx+2)
    ax.imshow(warped_orig, interpolation='none')
    ax.set_title('Perspective transform of the original image',  fontsize=14)
    ax.set_xticks([])
    ax.set_yticks([])
    ax = fig.add_subplot(NUM_ROWS,NUM_COLUMNS,3*idx+3)
    ax.imshow(warped_mask, interpolation='none', cmap='gray')
    ax.set_title('Perspective transform of the masked and thesholded image',  fontsize=14)
    ax.set_xticks([])
    ax.set_yticks([])
plt.tight_layout(w_pad=-0.3, h_pad=-0.2)
plt.suptitle('Applying perspective transform to the road images', y=1.02, fontsize=30)
plt.show()

2.3 Detecting lane lines and fitting polynomials

Given a bird's-eye-view binary image of the road, we now identify bottom positions of the lines and detect pixels that belong to the lanes. We use gaussian_filter1d to smoothen the pixel distributions, and find_peaks_cwt to identify maximum probable locations of the lane in an image.

In [18]:
from scipy.ndimage.filters import gaussian_filter1d
from scipy.ndimage.filters import gaussian_filter
from scipy.signal import find_peaks_cwt

def get_lane_peaks(img):
    img_height_half = int(img.shape[0]/2)
    histogram = np.sum(img[img_height_half:,:], axis=0)
    histogram = gaussian_filter1d(histogram, 40)
    peaks = find_peaks_cwt(histogram, np.arange(90,300))
    return histogram, peaks

Visualizing gaussian smoothing and peak detections for a test image:

In [19]:
img = mpimg.imread(images_road[6])
bin_img = image_pipiline_V_0_2(img)

histogram, peaks = get_lane_peaks(bin_img)
x= list(range(len(histogram)))
fig = plt.figure(figsize=(8,8))
ax1 = fig.add_subplot(2,1,1)
plt.imshow(gaussian_filter(bin_img, sigma=0.25, order=0), cmap='gray')
fig.add_subplot(2,1,2, sharex=ax1)
plt.plot(x, histogram)
for el in peaks:
    plt.plot(el, 0, 'd', color='red')
plt.tight_layout(w_pad=-0.3, h_pad=-0.2)
plt.show()

We now define a method get_lane, which performs initial scanning and detection of the lane lines. When we have initial polynomial coefficients detected, we use get_successive_poly_fit to perform search in the region of previously detected polynomial. We then fit second order polynomial to our lane line in a method get_lane_poly_fit.

In [47]:
def scan_lane_initial(img, peak, nwindows=9):
    # Set height of windows
    window_height = np.int(img.shape[0]/nwindows)
    # Identify the x and y positions of all nonzero pixels in the image
    out_img = np.dstack((img, img, img))*255
    nonzero = img.nonzero()
    nonzeroy = np.array(nonzero[0])
    nonzerox = np.array(nonzero[1])
    # Current positions to be updated for each window
    current_center = peak
    # Create empty lists to receive left and right lane pixel indices
    lane_inds = []
    # Step through the windows one by one
    for window in range(nwindows):
        good_inds, out_img, current_center = get_lane_segment(img, 
                                                              out_img, 
                                                              window, 
                                                              current_center, 
                                                              nonzerox, 
                                                              nonzeroy)
        lane_inds.append(good_inds)
    return out_img, lane_inds

def get_lane_segment(img, out_img, window, current_center, nonzerox, nonzeroy):
    # Set the width of the windows +/- margin
    margin = 140
    # Set minimum number of pixels found to recenter window
    minpix = 50
    nwindows=9
    window_height = np.int(img.shape[0]/nwindows)
    # Identify window boundaries in x and y (and right and left)
    win_y_low  = img.shape[0] - (window+1)*window_height
    win_y_high = img.shape[0] - window*window_height
    win_x_low  = current_center - margin
    win_x_high = current_center + margin
    # Draw the windows on the visualization image
    #cv2.rectangle(out_img,(win_x_low,win_y_low),(win_x_high,win_y_high),(0,255,0), 2) 
    # Identify the nonzero pixels in x and y within the window
    good_inds = ((nonzeroy >= win_y_low) & (nonzeroy < win_y_high) & 
    (nonzerox >= win_x_low) &  (nonzerox < win_x_high)).nonzero()[0]
    # Append these indices to the lists
    # If you found > minpix pixels, recenter next window on their mean position
    if len(good_inds) > minpix:
        current_center = np.int(np.mean(nonzerox[good_inds]))
    return good_inds, out_img, current_center

def get_lane(img, peak_idx):
    nonzero = img.nonzero()
    nonzeroy = np.array(nonzero[0])
    nonzerox = np.array(nonzero[1])
    histogram, peaks = get_lane_peaks(img)
    out_img, lane_inds = scan_lane_initial(img, peaks[peak_idx])
    lane = np.concatenate(lane_inds)
    lane_indices = nonzeroy[lane], nonzerox[lane]
    return out_img, lane_indices

def get_left_lane(img):
    return get_lane(img, 0)

def get_right_lane(img):
    return get_lane(img, 1)

def get_lane_poly_fit(img, peak_idx):
    nonzero = img.nonzero()
    nonzeroy = np.array(nonzero[0])
    nonzerox = np.array(nonzero[1])
    histogram, peaks = get_lane_peaks(img)
    _, lane_inds = scan_lane_initial(img, peaks[peak_idx])
    lane = np.concatenate(lane_inds)
    x = nonzerox[lane]
    y = nonzeroy[lane]
    poly_fit = np.polyfit(y, x, 2)
    poly_y = np.linspace(0, img.shape[0]-1, img.shape[0] )
    poly_x = poly_fit[0]*poly_y**2 + poly_fit[1]*poly_y + poly_fit[2]
    return poly_fit, poly_y, poly_x

def get_left_lane_poly_fit(img):
    return get_lane_poly_fit(img, 0)

def get_right_lane_poly_fit(img):
    return get_lane_poly_fit(img, 1)

def get_successive_poly_fit(img, poly_fit):
    nonzero = img.nonzero()
    nonzeroy = np.array(nonzero[0])
    nonzerox = np.array(nonzero[1])
    margin = 130
    lane_inds = ((nonzerox > (poly_fit[0]*(nonzeroy**2) + 
                              poly_fit[1]*nonzeroy + 
                              poly_fit[2] - margin)) & 
                 (nonzerox < (poly_fit[0]*(nonzeroy**2) + 
                              poly_fit[1]*nonzeroy + 
                              poly_fit[2] + margin)))
    x = nonzerox[lane_inds]
    y = nonzeroy[lane_inds] 
    new_poly_fit = np.polyfit(y, x, 2)
    poly_y = np.linspace(0, img.shape[0]-1, img.shape[0] )
    poly_x = new_poly_fit[0]*poly_y**2 + new_poly_fit[1]*poly_y + new_poly_fit[2]
    return new_poly_fit, poly_y, poly_x

We now visualize lane detection: we color pixels of the left lane in red, right lane in blue, and the corresponding fitted polynomial in yellow.

In [28]:
# Plotting undistored, bird's-eye-view, and detected lanes
NUM_COLUMNS = 3
NUM_ROAD_IMG = 3*len(images_road)
NUM_ROWS = math.ceil(NUM_ROAD_IMG / NUM_COLUMNS)
fig = plt.figure(figsize=(20,30))
for idx, fname in enumerate(images_road):
    img = mpimg.imread(fname)
    undist = camera.undistort(img)
    bin_img = image_pipiline_V_0_2(undist)
    out_img, right_lane_indices = get_right_lane(bin_img)
    out_img[right_lane_indices] = [0, 0, 255]
    _, left_lane_indices = get_left_lane(bin_img)
    out_img[left_lane_indices] = [255, 0, 0]
    left_poly_fit, left_poly_y, left_poly_x = get_left_lane_poly_fit(bin_img)
    right_poly_fit, right_poly_y, right_poly_x = get_right_lane_poly_fit(bin_img)
    ax = fig.add_subplot(NUM_ROWS,NUM_COLUMNS,3*idx+1)
    ax.imshow(undist, interpolation='none')
    ax.set_title('Undistorted road image',  fontsize=14)
    ax.set_xticks([])
    ax.set_yticks([])
    ax = fig.add_subplot(NUM_ROWS,NUM_COLUMNS,3*idx+2)
    ax.imshow(bin_img, interpolation='none', cmap='gray')
    ax.set_title('Bird\'s-eye-view of lane lines',  fontsize=14)
    ax.set_xticks([])
    ax.set_yticks([])
    ax = fig.add_subplot(NUM_ROWS,NUM_COLUMNS,3*idx+3)
    ax.imshow(out_img, interpolation='none', cmap='gray')
    plt.plot(left_poly_x, left_poly_y, color='yellow', lw=3)
    plt.plot(right_poly_x, right_poly_y, color='yellow', lw=3)
    ax.set_title('Left (red) and Right (blue) lane lines',  fontsize=14)
    ax.set_xticks([])
    ax.set_yticks([])
plt.tight_layout(w_pad=-0.3, h_pad=-0.2)
plt.suptitle('Identifying left and right lanes in to the road images', y=1.02, fontsize=30)
plt.show()
In [34]:
img = mpimg.imread(images_road[5])
undist = camera.undistort(img)
bin_img = image_pipiline_V_0_2(undist)
margin = 140
left_poly_fit, left_poly_y, left_poly_x = get_left_lane_poly_fit(bin_img)
new_left_poly_fit, new_left_poly_y, new_left_poly_x = get_successive_poly_fit(bin_img, left_poly_fit)
left_line_window1 = np.array([np.transpose(np.vstack([left_poly_x-margin, left_poly_y]))])
left_line_window2 = np.array([np.flipud(np.transpose(np.vstack([left_poly_x+margin, 
                              left_poly_y])))])
left_line_pts = np.hstack((left_line_window1, left_line_window2))
window_img = np.zeros_like(out_img)
cv2.fillPoly(window_img, np.int_([left_line_pts]), (0,255, 0))
#right lane
right_poly_fit, right_poly_y, right_poly_x = get_right_lane_poly_fit(bin_img)

new_right_poly_fit, new_right_poly_y, new_right_poly_x = get_successive_poly_fit(bin_img, right_poly_fit)
right_line_window1 = np.array([np.transpose(np.vstack([right_poly_x-margin, right_poly_y]))])
right_line_window2 = np.array([np.flipud(np.transpose(np.vstack([right_poly_x+margin, 
                               right_poly_y])))])
right_line_pts = np.hstack((right_line_window1, right_line_window2))
cv2.fillPoly(window_img, np.int_([right_line_pts]), (0,255, 0))
stacked_bin = np.dstack((bin_img, bin_img, bin_img))*255
result = cv2.addWeighted(stacked_bin, 1, window_img, 0.3, 0)
plt.imshow(result)
plt.plot(new_left_poly_x, new_left_poly_y, color='yellow', lw=3)
plt.plot(new_right_poly_x, new_right_poly_y, color='yellow', lw=3)
plt.show()

2.4 Measuring Curvature and vehicle offset

Given polynomial fits for each line, we detect radius of curvature (radius_of_curvature) and lane offset (get_lane_offset).

In [35]:
def radius_of_curvature(poly_y, poly_x, ym_per_pix=30/720, xm_per_pix=3.7/700):
    y_eval = np.max(poly_y)
    fit_cr = np.polyfit(poly_y*ym_per_pix, poly_x*xm_per_pix, 2)
    curverad = ((1 + (2*fit_cr[0]*y_eval*ym_per_pix + fit_cr[1])**2)**1.5) /  \
                    np.absolute(2*fit_cr[0])
    return curverad

left_rad = radius_of_curvature(left_poly_y, left_poly_x)
right_rad = radius_of_curvature(right_poly_y, right_poly_x)

print(left_rad, 'm', right_rad, 'm')
# Now our radius of curvature is in meters
1010.54050666 m 398.626481077 m
In [36]:
def get_lane_offset(poly_fit, poly_y):
    mid_point = 640
    xm_per_pix=3.7/690
    y_eval = max(poly_y)
    line_x = poly_fit[0]*y_eval**2 \
           + poly_fit[1]*y_eval \
           + poly_fit[2]
    offset_from_center = (line_x - mid_point)*xm_per_pix
    return offset_from_center

left_offset = get_lane_offset(left_poly_fit, left_poly_y)
right_offset = get_lane_offset(right_poly_fit, right_poly_y)
print(left_offset)
print(right_offset)
offset_from_center = left_offset + right_offset
if offset_from_center >= 0:
    print("Vehicle is {:.2f} m right of center".format(offset_from_center))
else:
    print("Vehicle is {:.2f} m left  of center".format(offset_from_center))
-1.41412275167
2.08203433968
Vehicle is 0.67 m right of center

We now fill the region between the two detected line and project the results back on the original image:

In [37]:
#Visualizing final result
def visualize_final(img):
    warp_zero = np.zeros_like(img).astype(np.uint8)
    color_warp = np.dstack((warp_zero, warp_zero, warp_zero))
    _, right_lane_indices = get_right_lane(img)
    _, left_lane_indices = get_left_lane(img)
    color_warp[right_lane_indices] = [255, 0, 0]
    color_warp[left_lane_indices] = [255, 0, 0]
    left_poly_fit, left_poly_y, left_poly_x = get_left_lane_poly_fit(img)
    right_poly_fit, right_poly_y, right_poly_x = get_right_lane_poly_fit(img)
    pts_left = np.array([np.transpose(np.vstack([left_poly_x, left_poly_y]))])
    pts_right = np.array([np.flipud(np.transpose(np.vstack([right_poly_x, right_poly_y])))])
    pts = np.hstack((pts_left, pts_right))
    cv2.fillPoly(color_warp, np.int_([pts]), (0,255, 0))
    # color_warp[right_lane_indices] = [255,0, 0]
    # Warp the blank back to original image space using inverse perspective matrix (Minv)
    new_warp = change_perspective(color_warp, inv=True)
    return new_warp

newwarp = visualize_final(bin_img)
result = cv2.addWeighted(undist, 1, newwarp, 0.2, 0)

font = cv2.FONT_HERSHEY_SIMPLEX
msg_rad = "Radius of Curvature = {:.1f} m".format( (left_rad + right_rad)/2 )
msg_off = "Vehicle is {:.2f} m from center".format( offset_from_center )
cv2.putText(result, msg_rad, (30,50),  font, 1.6,(255,255,255),2,cv2.LINE_AA)
cv2.putText(result, msg_off, (30,100), font, 1.6,(255,255,255),2,cv2.LINE_AA)
fig = plt.figure(figsize=(10,10))
plt.imshow(result)
plt.show()

2.5 Putting all the steps together

The method below takes original image and performs all the steps of the image pipeline:

  1. Undistort an image
  2. Apply thresholds (colorspace and Sobel)
  3. Zero out pixels that are outside of the predefined mask of the road
  4. Change perspective
  5. Detect right and left lanes
  6. Fit polynomials for right and left lanes
  7. Calculate curvature an offset
  8. Project result on undistored image
  9. Put the text messages
In [53]:
def pipeline_v_0_3(img):
    undist = camera.undistort(img)
    binary_thresh = np.uint8(apply_thresholds(undist))
    binary_masked = region_of_interest(binary_thresh)
    bin_img = change_perspective(binary_masked)
    out_img, right_lane_indices = get_right_lane(bin_img)
    out_img[right_lane_indices] = [0, 0, 255]
    _, left_lane_indices = get_left_lane(bin_img)
    out_img[left_lane_indices] = [255, 0, 0]
    left_poly_fit, left_poly_y, left_poly_x = get_left_lane_poly_fit(bin_img)
    right_poly_fit, right_poly_y, right_poly_x = get_right_lane_poly_fit(bin_img)
    left_rad = radius_of_curvature(left_poly_y, left_poly_x)
    right_rad = radius_of_curvature(right_poly_y, right_poly_x)
    left_offset = get_lane_offset(left_poly_fit, left_poly_y)
    right_offset = get_lane_offset(right_poly_fit, right_poly_y)
    offset_from_center = left_offset + right_offset
    newwarp = visualize_final(bin_img)   
    result = cv2.addWeighted(undist, 1, newwarp, 0.2, 0)
    font = cv2.FONT_HERSHEY_SIMPLEX
    msg_rad = "Radius of Curvature = {:.1f} m".format( (left_rad + right_rad)/2 )
    msg_off = "Vehicle is {:.2f} m from center".format( offset_from_center )
    cv2.putText(result, msg_rad, (30,50),  font, 1.6,(255,255,255),2,cv2.LINE_AA)
    cv2.putText(result, msg_off, (30,100), font, 1.6,(255,255,255),2,cv2.LINE_AA)
    return result

2.6 Visualizing results of image pipeline

We now apply the image pipeline to all the images from the test_images folder and display the output.

In [59]:
# Plotting undistored, bird's-eye-view, and detected lanes
NUM_COLUMNS = 1
NUM_ROAD_IMG = len(images_road)
NUM_ROWS = math.ceil(NUM_ROAD_IMG / NUM_COLUMNS)
fig = plt.figure(figsize=(10,40))
for idx, fname in enumerate(images_road):
    img = mpimg.imread(fname)
    result = pipeline_v_0_3(img)
    ax = fig.add_subplot(NUM_ROWS,NUM_COLUMNS,idx+1)
    ax.imshow(result)
    ax.set_title('Processed road image: {}'.format(fname),  fontsize=14)
    ax.set_xticks([])
    ax.set_yticks([])
plt.tight_layout(w_pad=-0.3, h_pad=-0.2)
plt.suptitle('Showcasing image pipeline on individual images', y=1.02, fontsize=30)
plt.show()

3. Object oriented design

In order to correctly process video stream, and get our design extensible for future, we define three classes: Line, ImageHandler and VideoHandler. The Line class holds polynomial coefficients, radius of curvature and vehicle offset. ImageHandler class holds all the methods from image processing pipeline, and accepts a color image from VideoHandler, which in turn applies ImageHandler.process_image method for each frame of the video clip.

In [61]:
# Import everything needed to edit/save/watch video clips
from moviepy.editor import VideoFileClip
from IPython.display import HTML
from collections import deque
In [62]:
class Line():
    def __init__(self):
        self.N_WINDOW = 13
        self.poly_fit  = deque([], self.N_WINDOW)
        self.poly_y    = deque([], self.N_WINDOW)
        self.poly_x    = deque([], self.N_WINDOW)
        self.curvature = deque([], self.N_WINDOW)
        self.offset    = deque([], self.N_WINDOW)
    
    #write method to 1) 
    def set_current_poly_fit(self, poly_fit, poly_y, poly_x):
        self.poly_fit.append(poly_fit)
        self.poly_y.append(poly_y)
        self.poly_x.append(poly_x)

    def set_current_curvature(self, rad):
        self.curvature.append(rad)
        
    def set_current_offset(self, offset):
        self.offset.append(offset)
        
    def get_curvature(self):
        return sum(self.curvature)/self.N_WINDOW
    
    def get_offset(self):
        return sum(self.offset)/self.N_WINDOW
In [63]:
class ImageHandler():
    def __init__(self, *args, **kwargs):
        self.left_line = Line()  
        self.right_line = Line()
        self.camera = Camera()
    
    def process_image(self, img):
        undist = self.camera.undistort(img)
        binary_thresh = np.uint8(self.apply_thresholds(undist))
        binary_masked = self.region_of_interest(binary_thresh)
        bin_img = self.change_perspective(binary_masked)
        out_img, right_lane_indices = self.get_right_lane(bin_img)
        out_img[right_lane_indices] = [0, 0, 255]
        _, left_lane_indices = self.get_left_lane(bin_img)
        out_img[left_lane_indices] = [255, 0, 0]
        left_poly_fit, left_poly_y, left_poly_x = self.get_left_lane_poly_fit(bin_img)
        right_poly_fit, right_poly_y, right_poly_x = self.get_right_lane_poly_fit(bin_img)
        left_rad = self.radius_of_curvature(left_poly_y, left_poly_x)
        right_rad = self.radius_of_curvature(right_poly_y, right_poly_x)
        left_offset = self.get_lane_offset(left_poly_fit, left_poly_y)
        right_offset = self.get_lane_offset(right_poly_fit, right_poly_y)
        self.left_line.set_current_poly_fit(left_poly_fit, left_poly_y, left_poly_x)
        self.right_line.set_current_poly_fit(right_poly_fit, right_poly_y, right_poly_x)
        self.left_line.set_current_curvature(left_rad)
        self.right_line.set_current_curvature(right_rad)
        self.left_line.set_current_offset(left_offset)
        self.right_line.set_current_offset(right_offset)
        avg_left_offset = self.left_line.get_offset()
        avg_right_offset = self.right_line.get_offset()
        avg_left_rad = self.left_line.get_curvature()
        avg_right_rad = self.right_line.get_curvature()
        offset_from_center = avg_left_offset + avg_right_offset
        newwarp = self.visualize_final(bin_img)   
        result = cv2.addWeighted(undist, 1, newwarp, 0.2, 0)
        font = cv2.FONT_HERSHEY_SIMPLEX
        msg_rad = "Radius of Curvature = {:.1f} m".format( (avg_left_rad + avg_right_rad)/2 )
        msg_off = "Vehicle is {:.2f} m from center".format( offset_from_center )
        cv2.putText(result, msg_rad, (30,50),  font, 1.6,(255,255,255),2,cv2.LINE_AA)
        cv2.putText(result, msg_off, (30,100), font, 1.6,(255,255,255),2,cv2.LINE_AA)
        return result
    
    def abs_sobel_thresh(self, image, orient='x', sobel_kernel=3, thresh=(0, 255)):
        # Calculate directional gradient: x or y 
        # Apply threshold
        gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
        # Apply x or y gradient with the OpenCV Sobel() function
        # and take the absolute value
        if orient == 'x':
            abs_sobel = np.absolute(cv2.Sobel(gray, cv2.CV_64F, 1, 0))
        if orient == 'y':
            abs_sobel = np.absolute(cv2.Sobel(gray, cv2.CV_64F, 0, 1))
        # Rescale back to 8 bit integer
        scaled_sobel = np.uint8(255*abs_sobel/np.max(abs_sobel))
        # Create a copy and apply the threshold
        grad_binary = np.zeros_like(scaled_sobel)
        grad_binary[(scaled_sobel >= thresh[0]) & (scaled_sobel <= thresh[1])] = 1
        return grad_binary
    
    def mag_thresh(self, image, sobel_kernel=3, mag_thresh=(0, 255)):
        # Calculate gradient magnitude (l2-norm)
        # Convert to grayscale
        gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
        # Take both Sobel x and y gradients
        sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=sobel_kernel)
        sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=sobel_kernel)
        # Calculate the gradient magnitude
        gradmag = np.sqrt(sobelx**2 + sobely**2)
        # Rescale to 8 bit
        scale_factor = np.max(gradmag)/255 
        gradmag = (gradmag/scale_factor).astype(np.uint8) 
        # Create a binary image of ones where threshold is met, zeros otherwise
        mag_binary = np.zeros_like(gradmag)
        mag_binary[(gradmag >= mag_thresh[0]) & (gradmag <= mag_thresh[1])] = 1
        return mag_binary
    
    def dir_threshold(self, image, sobel_kernel=3, thresh=(0, np.pi/2)):
        # Calculate gradient direction -- arctan(y/x)
        # Apply threshold
        # 1) Convert to grayscale
        gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
        # 2) Take the gradient in x and y separately
        sobel_x = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize = sobel_kernel)
        sobel_y = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize = sobel_kernel)
        # 3) Take the absolute value of the x and y gradients
        abs_sobel_x = np.absolute(sobel_x)
        abs_sobel_y = np.absolute(sobel_y)
        # 4) Use np.arctan2(abs_sobely, abs_sobelx) to calculate the direction of the gradient 
        grad_dir = np.arctan2(abs_sobel_y, abs_sobel_x)
        # 5) Create a binary mask where direction thresholds are met
        dir_binary = np.zeros_like(grad_dir)
        # 6) Return this mask as your binary_output image
        dir_binary[(grad_dir >= thresh[0]) & (grad_dir <= thresh[1])] = 1
        return dir_binary
    
    def threshold_image(self, image, x_kernel=3, x_thresh=(30, 110), y_kernel=3, y_thresh=(40,110),
                               m_kernel=3, m_thresh=(30, 80),  d_kernel=7, d_thresh=(0.8,1.3)):
        gradx = self.abs_sobel_thresh(image, orient='x', sobel_kernel=x_kernel, thresh=x_thresh)
        grady = self.abs_sobel_thresh(image, orient='y', sobel_kernel=y_kernel, thresh=y_thresh)
        mag_binary = self.mag_thresh(image, sobel_kernel=m_kernel, mag_thresh=m_thresh)
        dir_binary = self.dir_threshold(image, sobel_kernel=d_kernel, thresh=d_thresh)
        
        combined = np.zeros_like(dir_binary)
        #combined[((gradx == 1) & (grady == 1)) | ((mag_binary == 1) & (dir_binary == 1))] = 1
        combined[((gradx == 1)) | ((mag_binary == 1)) & (dir_binary == 1)] = 1
    
        return combined
    
    def hls_threshold(self, img, s_thresh=(130, 255), l_thresh=(90,255)):
        hls = cv2.cvtColor(img, cv2.COLOR_RGB2HLS)
        l_channel = hls[:,:,1]
        s_channel = hls[:,:,2]
        s_binary = np.zeros_like(s_channel)
        s_binary[(s_channel >= s_thresh[0]) & (s_channel <= s_thresh[1])] = 1
        l_binary = np.zeros_like(l_channel)
        l_binary[(l_channel >= l_thresh[0]) & (l_channel <= l_thresh[1])] = 1
        return s_binary, l_binary
    
    def apply_thresholds(self, img):
        s_binary, l_binary = self.hls_threshold(img)
        out_img = self.threshold_image(img)
        combined_binary = np.zeros_like(out_img)
        combined_binary[ (l_binary == 1) & (s_binary == 1) | (out_img == 1)] = 1
        return combined_binary
    
    def get_color_binary(self, img):
        s_binary, l_binary = self.hls_threshold(img)
        # Stack each channel to view their individual contributions in green and blue respectively
        # This returns a stack of the two binary images, whose components you can see as different colors
        color_binary = np.uint8(np.dstack(( np.zeros_like(s_binary), l_binary, s_binary)) * 255)
        return color_binary
    
    def vertices_img_pipeline(self, img):
        """Define source and destination vertices for perspective transform
        """
        w = img.shape[1]
        h = img.shape[0]
        d_w = w/25
        d_h = 95
        bottom_w_offset = 425
        bottom_h_offset = 15
        w_offset = 0
        src_v1 = [w/2 + w_offset - bottom_w_offset, h - bottom_h_offset]
        src_v2 = [w/2 + w_offset - d_w, h/2 + d_h ]
        src_v3 = [w/2 + w_offset + d_w, h/2 + d_h ]
        src_v4 = [w/2 + w_offset + bottom_w_offset, h - bottom_h_offset]
        src_vertices = np.array( [[src_v1, src_v2, src_v3, src_v4]], dtype=np.float32 )
        dst_offset = 110
        dst_v1 = [src_vertices[0,0,0] + dst_offset, 720]
        dst_v2 = [src_vertices[0,0,0] + dst_offset, 0]
        dst_v3 = [src_vertices[0,3,0] - dst_offset, 0]
        dst_v4 = [src_vertices[0,3,0] - dst_offset, 720]
        dst_vertices = np.array( [[dst_v1, dst_v2, dst_v3, dst_v4]], dtype=np.float32 )
        return src_vertices, dst_vertices
    
    def region_of_interest(self, img):
        """
        Applies an image mask.
        
        Only keeps the region of the image defined by the polygon
        formed from `vertices`. The rest of the image is set to black.
        """
        src_vertices, _ = self.vertices_img_pipeline(img)
    
        src_v1 = src_vertices[0,0]
        src_v2 = src_vertices[0,1]
        src_v3 = src_vertices[0,2]
        src_v4 = src_vertices[0,3]
        mask_w_bottom_offset = 100
        mask_w_top_offset = 5
        mask_h_top_offset = 10
        mask_v1 = [src_v1[0] - mask_w_bottom_offset, src_v1[1]]
        mask_v2 = [src_v2[0] - 5, src_v2[1] - mask_h_top_offset]
        mask_v3 = [src_v3[0] + 30, src_v3[1] - mask_h_top_offset]
        mask_v4 = [src_v4[0] + mask_w_bottom_offset, src_v4[1]]
        vertices_mask = np.array([[mask_v1, mask_v2, mask_v3, mask_v4]], dtype=np.int32)
        
        #defining a blank mask to start with
        mask = np.zeros_like(img)   
        
        #defining a 3 channel or 1 channel color to fill the mask with depending on the input image
        if len(img.shape) > 2:
            channel_count = img.shape[2]  # i.e. 3 or 4 depending on your image
            ignore_mask_color = (255,) * channel_count
        else:
            ignore_mask_color = 255
            
        #filling pixels inside the polygon defined by "vertices" with the fill color    
        cv2.fillPoly(mask, vertices_mask, ignore_mask_color)
        
        #returning the image only where mask pixels are nonzero
        masked_image = cv2.bitwise_and(img, mask)
        return masked_image
    
    def change_perspective(self, img, inv=False):
        src, dst = self.vertices_img_pipeline(img)
        if not inv:
            M = cv2.getPerspectiveTransform(src, dst)
        else:
            M = cv2.getPerspectiveTransform(dst, src)
        img_size = (img.shape[1], img.shape[0])
        warped = cv2.warpPerspective(img, M, img_size, flags=cv2.INTER_LINEAR)
        return warped
    
    def get_lane_peaks(self, img):
        img_height_half = int(img.shape[0]/2)
        histogram = np.sum(img[img_height_half:,:], axis=0)
        histogram = gaussian_filter1d(histogram, 40)
        peaks = find_peaks_cwt(histogram, np.arange(90,300))
        return histogram, peaks
    
    def scan_lane_initial(self, img, peak, nwindows=9):
        # Set height of windows
        window_height = np.int(img.shape[0]/nwindows)
        # Identify the x and y positions of all nonzero pixels in the image
        out_img = np.dstack((img, img, img))*255
        nonzero = img.nonzero()
        nonzeroy = np.array(nonzero[0])
        nonzerox = np.array(nonzero[1])
        # Current positions to be updated for each window
        current_center = peak
        # Create empty lists to receive left and right lane pixel indices
        lane_inds = []
        # Step through the windows one by one
        for window in range(nwindows):
            good_inds, out_img, current_center = self.get_lane_segment(img, 
                                                                  out_img, 
                                                                  window, 
                                                                  current_center, 
                                                                  nonzerox, 
                                                                  nonzeroy)
            lane_inds.append(good_inds)
        return out_img, lane_inds
    
    def get_lane_segment(self, img, out_img, window, current_center, nonzerox, nonzeroy):
        # Set the width of the windows +/- margin
        margin = 140
        # Set minimum number of pixels found to recenter window
        minpix = 50
        nwindows=9
        window_height = np.int(img.shape[0]/nwindows)
        # Identify window boundaries in x and y (and right and left)
        win_y_low  = img.shape[0] - (window+1)*window_height
        win_y_high = img.shape[0] - window*window_height
        win_x_low  = current_center - margin
        win_x_high = current_center + margin
        # Draw the windows on the visualization image
        #cv2.rectangle(out_img,(win_x_low,win_y_low),(win_x_high,win_y_high),(0,255,0), 2) 
        # Identify the nonzero pixels in x and y within the window
        good_inds = ((nonzeroy >= win_y_low) & (nonzeroy < win_y_high) & 
        (nonzerox >= win_x_low) &  (nonzerox < win_x_high)).nonzero()[0]
        # Append these indices to the lists
        # If you found > minpix pixels, recenter next window on their mean position
        if len(good_inds) > minpix:
            current_center = np.int(np.mean(nonzerox[good_inds]))
        return good_inds, out_img, current_center
    
    def get_lane(self, img, peak_idx):
        nonzero = img.nonzero()
        nonzeroy = np.array(nonzero[0])
        nonzerox = np.array(nonzero[1])
        histogram, peaks = self.get_lane_peaks(img)
        out_img, lane_inds = self.scan_lane_initial(img, peaks[peak_idx])
        lane = np.concatenate(lane_inds)
        lane_indices = nonzeroy[lane], nonzerox[lane]
        return out_img, lane_indices
    
    def get_left_lane(self, img):
        return self.get_lane(img, 0)
    
    def get_right_lane(self, img):
        return self.get_lane(img, 1)
    
    def get_lane_poly_fit(self, img, peak_idx):
        nonzero = img.nonzero()
        nonzeroy = np.array(nonzero[0])
        nonzerox = np.array(nonzero[1])
        histogram, peaks = self.get_lane_peaks(img)
        _, lane_inds = self.scan_lane_initial(img, peaks[peak_idx])
        lane = np.concatenate(lane_inds)
        x = nonzerox[lane]
        y = nonzeroy[lane]
        poly_fit = np.polyfit(y, x, 2)
        poly_y = np.linspace(0, img.shape[0]-1, img.shape[0] )
        poly_x = poly_fit[0]*poly_y**2 + poly_fit[1]*poly_y + poly_fit[2]
        return poly_fit, poly_y, poly_x
    
    def get_left_lane_poly_fit(self, img):
        return self.get_lane_poly_fit(img, 0)
    
    def get_right_lane_poly_fit(self, img):
        return self.get_lane_poly_fit(img, 1)
    
    def get_successive_poly_fit(self, img, poly_fit):
        nonzero = img.nonzero()
        nonzeroy = np.array(nonzero[0])
        nonzerox = np.array(nonzero[1])
        margin = 130
        lane_inds = ((nonzerox > (poly_fit[0]*(nonzeroy**2) + 
                                  poly_fit[1]*nonzeroy + 
                                  poly_fit[2] - margin)) & 
                     (nonzerox < (poly_fit[0]*(nonzeroy**2) + 
                                  poly_fit[1]*nonzeroy + 
                                  poly_fit[2] + margin)))
        x = nonzerox[lane_inds]
        y = nonzeroy[lane_inds] 
        new_poly_fit = np.polyfit(y, x, 2)
        poly_y = np.linspace(0, img.shape[0]-1, img.shape[0] )
        poly_x = new_poly_fit[0]*poly_y**2 + new_poly_fit[1]*poly_y + new_poly_fit[2]
        return new_poly_fit, poly_y, poly_x
    
    def radius_of_curvature(self, poly_y, poly_x, ym_per_pix=30/720, xm_per_pix=3.7/700):
        y_eval = np.max(poly_y)
        fit_cr = np.polyfit(poly_y*ym_per_pix, poly_x*xm_per_pix, 2)
        curverad = ((1 + (2*fit_cr[0]*y_eval*ym_per_pix + fit_cr[1])**2)**1.5) /  \
                        np.absolute(2*fit_cr[0])
        return curverad
    
    def get_lane_offset(self, poly_fit, poly_y):
        mid_point = 640
        xm_per_pix=3.7/690
        y_eval = max(poly_y)
        line_x = poly_fit[0]*y_eval**2 \
               + poly_fit[1]*y_eval \
               + poly_fit[2]
        offset_from_center = (line_x - mid_point)*xm_per_pix
        return offset_from_center
    
    def visualize_final(self, img):
        warp_zero = np.zeros_like(img).astype(np.uint8)
        color_warp = np.dstack((warp_zero, warp_zero, warp_zero))
        _, right_lane_indices = self.get_right_lane(img)
        _, left_lane_indices = self.get_left_lane(img)
        color_warp[right_lane_indices] = [255, 0, 0]
        color_warp[left_lane_indices] = [255, 0, 0]
        left_poly_fit, left_poly_y, left_poly_x = self.get_left_lane_poly_fit(img)
        right_poly_fit, right_poly_y, right_poly_x = self.get_right_lane_poly_fit(img)
        pts_left = np.array([np.transpose(np.vstack([left_poly_x, left_poly_y]))])
        pts_right = np.array([np.flipud(np.transpose(np.vstack([right_poly_x, right_poly_y])))])
        pts = np.hstack((pts_left, pts_right))
        cv2.fillPoly(color_warp, np.int_([pts]), (0,255, 0))
        # color_warp[right_lane_indices] = [255,0, 0]
        # Warp the blank back to original image space using inverse perspective matrix (Minv)
        new_warp = self.change_perspective(color_warp, inv=True)
        return new_warp
In [85]:
class VideoHandler():
    
    def __init__(self, *args, **kwargs):
        self.clip_name = args[0]["input_video"]
        self.subclip_length = args[0]["subclip_length"]
        self.out_clip = None
        self.out_clip_name = args[0]["output_video"]
#         self.video_clip = VideoFileClip(self.clip_name).subclip(0, self.subclip_length)
#         self.video_clip = VideoFileClip(self.clip_name).subclip(20, 25)
#         self.video_clip = VideoFileClip(self.clip_name).subclip(23, 25)
#         self.video_clip = VideoFileClip(self.clip_name).subclip(38, 42)
        self.video_clip = VideoFileClip(self.clip_name)
        self.frame_counter = 0
        self.image_handler = ImageHandler()
    
    def process_video(self):
        self.out_clip = self.video_clip.fl_image(self.process_image) #NOTE: this function expects color images!!
        self.out_clip.write_videofile(self.out_clip_name, audio=False)
    
    def process_image(self, img):
        self.frame_counter += 1
        out_img = self.image_handler.process_image(img)
#         print("Processing frame: {} of shape {}".format(self.frame_counter, img.shape))
        return out_img

4. Video processing pipeline

The method below invoked the video processing pipeline, and creates project_video_out.mp4 video clip.

In [86]:
from collections import OrderedDict
def process_video_v():
    print("Starting video processing")
    args = OrderedDict()
    args["input_video"] = 'project_video.mp4'
    args["output_video"] = 'project_video_out.mp4'
    args["subclip_length"] = 9
    video_handler = VideoHandler(args)
    print("video_handler: input  {}".format(video_handler.clip_name))
    print("video_handler: output {}".format(video_handler.out_clip_name))
    video_handler.process_video()

process_video_v()
Starting video processing
video_handler: input  project_video.mp4
video_handler: output project_video_out.mp4
[MoviePy] >>>> Building video project_video_out.mp4
[MoviePy] Writing video project_video_out.mp4
  0%|          | 0/1261 [00:00<?, ?it/s]
  0%|          | 1/1261 [00:01<24:33,  1.17s/it]
  0%|          | 2/1261 [00:02<24:35,  1.17s/it]
  0%|          | 3/1261 [00:03<24:36,  1.17s/it]
  0%|          | 4/1261 [00:04<24:25,  1.17s/it]
  0%|          | 5/1261 [00:05<24:10,  1.16s/it]
  0%|          | 6/1261 [00:06<24:02,  1.15s/it]
  1%|          | 7/1261 [00:08<23:55,  1.15s/it]
  1%|          | 8/1261 [00:09<23:53,  1.14s/it]
  1%|          | 9/1261 [00:10<23:41,  1.14s/it]
  1%|          | 10/1261 [00:11<23:50,  1.14s/it]
  1%|          | 11/1261 [00:12<23:46,  1.14s/it]
  1%|          | 12/1261 [00:13<23:33,  1.13s/it]
  1%|          | 13/1261 [00:14<23:33,  1.13s/it]
  1%|          | 14/1261 [00:16<23:35,  1.14s/it]
  1%|          | 15/1261 [00:17<23:42,  1.14s/it]
  1%|▏         | 16/1261 [00:18<24:05,  1.16s/it]
  1%|▏         | 17/1261 [00:19<24:02,  1.16s/it]
  1%|▏         | 18/1261 [00:20<24:01,  1.16s/it]
  2%|▏         | 19/1261 [00:21<23:56,  1.16s/it]
  2%|▏         | 20/1261 [00:23<24:06,  1.17s/it]
  2%|▏         | 21/1261 [00:24<23:52,  1.16s/it]
  2%|▏         | 22/1261 [00:25<23:42,  1.15s/it]
  2%|▏         | 23/1261 [00:26<23:26,  1.14s/it]
  2%|▏         | 24/1261 [00:27<23:24,  1.14s/it]
  2%|▏         | 25/1261 [00:28<23:24,  1.14s/it]
  2%|▏         | 26/1261 [00:29<23:58,  1.16s/it]
  2%|▏         | 27/1261 [00:31<23:48,  1.16s/it]
  2%|▏         | 28/1261 [00:32<23:36,  1.15s/it]
  2%|▏         | 29/1261 [00:33<23:39,  1.15s/it]
  2%|▏         | 30/1261 [00:34<23:33,  1.15s/it]
  2%|▏         | 31/1261 [00:35<24:21,  1.19s/it]
  3%|▎         | 32/1261 [00:36<24:16,  1.19s/it]
  3%|▎         | 33/1261 [00:38<23:54,  1.17s/it]
  3%|▎         | 34/1261 [00:39<23:41,  1.16s/it]
  3%|▎         | 35/1261 [00:40<25:10,  1.23s/it]
  3%|▎         | 36/1261 [00:41<24:44,  1.21s/it]
  3%|▎         | 37/1261 [00:42<24:18,  1.19s/it]
  3%|▎         | 38/1261 [00:44<24:04,  1.18s/it]
  3%|▎         | 39/1261 [00:45<23:50,  1.17s/it]
  3%|▎         | 40/1261 [00:46<24:43,  1.22s/it]
  3%|▎         | 41/1261 [00:47<25:34,  1.26s/it]
  3%|▎         | 42/1261 [00:49<24:47,  1.22s/it]
  3%|▎         | 43/1261 [00:50<24:58,  1.23s/it]
  3%|▎         | 44/1261 [00:51<25:10,  1.24s/it]
  4%|▎         | 45/1261 [00:53<26:37,  1.31s/it]
  4%|▎         | 46/1261 [00:54<26:26,  1.31s/it]
  4%|▎         | 47/1261 [00:55<25:26,  1.26s/it]
  4%|▍         | 48/1261 [00:56<25:10,  1.24s/it]
  4%|▍         | 49/1261 [00:57<24:39,  1.22s/it]
  4%|▍         | 50/1261 [00:59<24:25,  1.21s/it]
  4%|▍         | 51/1261 [01:00<23:56,  1.19s/it]
  4%|▍         | 52/1261 [01:01<24:23,  1.21s/it]
  4%|▍         | 53/1261 [01:02<24:44,  1.23s/it]
  4%|▍         | 54/1261 [01:03<24:59,  1.24s/it]
  4%|▍         | 55/1261 [01:05<24:08,  1.20s/it]
  4%|▍         | 56/1261 [01:06<23:56,  1.19s/it]
  5%|▍         | 57/1261 [01:07<23:29,  1.17s/it]
  5%|▍         | 58/1261 [01:08<23:19,  1.16s/it]
  5%|▍         | 59/1261 [01:09<22:57,  1.15s/it]
  5%|▍         | 60/1261 [01:10<23:45,  1.19s/it]
  5%|▍         | 61/1261 [01:12<24:18,  1.22s/it]
  5%|▍         | 62/1261 [01:13<25:15,  1.26s/it]
  5%|▍         | 63/1261 [01:14<24:24,  1.22s/it]
  5%|▌         | 64/1261 [01:15<23:45,  1.19s/it]
  5%|▌         | 65/1261 [01:16<23:16,  1.17s/it]
  5%|▌         | 66/1261 [01:18<22:55,  1.15s/it]
  5%|▌         | 67/1261 [01:19<23:09,  1.16s/it]
  5%|▌         | 68/1261 [01:20<22:56,  1.15s/it]
  5%|▌         | 69/1261 [01:21<23:24,  1.18s/it]
  6%|▌         | 70/1261 [01:22<24:03,  1.21s/it]
  6%|▌         | 71/1261 [01:24<23:57,  1.21s/it]
  6%|▌         | 72/1261 [01:25<23:53,  1.21s/it]
  6%|▌         | 73/1261 [01:26<23:49,  1.20s/it]
  6%|▌         | 74/1261 [01:27<25:02,  1.27s/it]
  6%|▌         | 75/1261 [01:29<24:42,  1.25s/it]
  6%|▌         | 76/1261 [01:30<24:16,  1.23s/it]
  6%|▌         | 77/1261 [01:31<24:29,  1.24s/it]
  6%|▌         | 78/1261 [01:32<24:34,  1.25s/it]
  6%|▋         | 79/1261 [01:34<25:08,  1.28s/it]
  6%|▋         | 80/1261 [01:35<25:38,  1.30s/it]
  6%|▋         | 81/1261 [01:36<24:44,  1.26s/it]
  7%|▋         | 82/1261 [01:37<24:22,  1.24s/it]
  7%|▋         | 83/1261 [01:39<23:58,  1.22s/it]
  7%|▋         | 84/1261 [01:40<24:19,  1.24s/it]
  7%|▋         | 85/1261 [01:41<25:26,  1.30s/it]
  7%|▋         | 86/1261 [01:42<24:30,  1.25s/it]
  7%|▋         | 87/1261 [01:44<25:31,  1.30s/it]
  7%|▋         | 88/1261 [01:45<25:09,  1.29s/it]
  7%|▋         | 89/1261 [01:47<26:56,  1.38s/it]
  7%|▋         | 90/1261 [01:48<27:17,  1.40s/it]
  7%|▋         | 91/1261 [01:50<28:02,  1.44s/it]
  7%|▋         | 92/1261 [01:51<29:14,  1.50s/it]
  7%|▋         | 93/1261 [01:53<27:51,  1.43s/it]
  7%|▋         | 94/1261 [01:54<26:15,  1.35s/it]
  8%|▊         | 95/1261 [01:55<25:07,  1.29s/it]
  8%|▊         | 96/1261 [01:56<24:23,  1.26s/it]
  8%|▊         | 97/1261 [01:57<23:46,  1.23s/it]
  8%|▊         | 98/1261 [01:58<23:17,  1.20s/it]
  8%|▊         | 99/1261 [01:59<22:55,  1.18s/it]
  8%|▊         | 100/1261 [02:01<22:41,  1.17s/it]
  8%|▊         | 101/1261 [02:02<22:39,  1.17s/it]
  8%|▊         | 102/1261 [02:03<22:31,  1.17s/it]
  8%|▊         | 103/1261 [02:04<23:36,  1.22s/it]
  8%|▊         | 104/1261 [02:06<23:33,  1.22s/it]
  8%|▊         | 105/1261 [02:07<25:38,  1.33s/it]
  8%|▊         | 106/1261 [02:08<24:54,  1.29s/it]
  8%|▊         | 107/1261 [02:10<24:40,  1.28s/it]
  9%|▊         | 108/1261 [02:11<24:26,  1.27s/it]
  9%|▊         | 109/1261 [02:12<23:42,  1.24s/it]
  9%|▊         | 110/1261 [02:13<23:06,  1.20s/it]
  9%|▉         | 111/1261 [02:14<22:39,  1.18s/it]
  9%|▉         | 112/1261 [02:15<22:28,  1.17s/it]
  9%|▉         | 113/1261 [02:17<22:15,  1.16s/it]
  9%|▉         | 114/1261 [02:18<22:02,  1.15s/it]
  9%|▉         | 115/1261 [02:19<22:27,  1.18s/it]
  9%|▉         | 116/1261 [02:20<22:15,  1.17s/it]
  9%|▉         | 117/1261 [02:21<22:04,  1.16s/it]
  9%|▉         | 118/1261 [02:22<22:00,  1.16s/it]
  9%|▉         | 119/1261 [02:24<22:57,  1.21s/it]
 10%|▉         | 120/1261 [02:25<22:41,  1.19s/it]
 10%|▉         | 121/1261 [02:26<23:03,  1.21s/it]
 10%|▉         | 122/1261 [02:27<23:06,  1.22s/it]
 10%|▉         | 123/1261 [02:28<22:44,  1.20s/it]
 10%|▉         | 124/1261 [02:30<22:30,  1.19s/it]
 10%|▉         | 125/1261 [02:31<23:13,  1.23s/it]
 10%|▉         | 126/1261 [02:32<23:13,  1.23s/it]
 10%|█         | 127/1261 [02:33<23:22,  1.24s/it]
 10%|█         | 128/1261 [02:35<23:13,  1.23s/it]
 10%|█         | 129/1261 [02:36<22:44,  1.21s/it]
 10%|█         | 130/1261 [02:37<22:35,  1.20s/it]
 10%|█         | 131/1261 [02:38<22:13,  1.18s/it]
 10%|█         | 132/1261 [02:39<22:07,  1.18s/it]
 11%|█         | 133/1261 [02:41<22:52,  1.22s/it]
 11%|█         | 134/1261 [02:42<23:12,  1.24s/it]
 11%|█         | 135/1261 [02:43<23:01,  1.23s/it]
 11%|█         | 136/1261 [02:44<22:35,  1.20s/it]
 11%|█         | 137/1261 [02:45<22:27,  1.20s/it]
 11%|█         | 138/1261 [02:47<22:18,  1.19s/it]
 11%|█         | 139/1261 [02:48<21:58,  1.18s/it]
 11%|█         | 140/1261 [02:49<21:51,  1.17s/it]
 11%|█         | 141/1261 [02:50<21:56,  1.18s/it]
 11%|█▏        | 142/1261 [02:51<21:54,  1.17s/it]
 11%|█▏        | 143/1261 [02:52<21:49,  1.17s/it]
 11%|█▏        | 144/1261 [02:54<21:44,  1.17s/it]
 11%|█▏        | 145/1261 [02:55<21:47,  1.17s/it]
 12%|█▏        | 146/1261 [02:56<21:44,  1.17s/it]
 12%|█▏        | 147/1261 [02:57<21:48,  1.17s/it]
 12%|█▏        | 148/1261 [02:58<21:54,  1.18s/it]
 12%|█▏        | 149/1261 [02:59<21:45,  1.17s/it]
 12%|█▏        | 150/1261 [03:01<21:37,  1.17s/it]
 12%|█▏        | 151/1261 [03:02<21:35,  1.17s/it]
 12%|█▏        | 152/1261 [03:03<21:40,  1.17s/it]
 12%|█▏        | 153/1261 [03:04<21:48,  1.18s/it]
 12%|█▏        | 154/1261 [03:05<21:39,  1.17s/it]
 12%|█▏        | 155/1261 [03:06<21:30,  1.17s/it]
 12%|█▏        | 156/1261 [03:08<21:21,  1.16s/it]
 12%|█▏        | 157/1261 [03:09<21:18,  1.16s/it]
 13%|█▎        | 158/1261 [03:10<21:05,  1.15s/it]
 13%|█▎        | 159/1261 [03:11<21:19,  1.16s/it]
 13%|█▎        | 160/1261 [03:12<22:10,  1.21s/it]
 13%|█▎        | 161/1261 [03:14<21:59,  1.20s/it]
 13%|█▎        | 162/1261 [03:15<21:42,  1.18s/it]
 13%|█▎        | 163/1261 [03:16<21:27,  1.17s/it]
 13%|█▎        | 164/1261 [03:17<21:22,  1.17s/it]
 13%|█▎        | 165/1261 [03:18<21:11,  1.16s/it]
 13%|█▎        | 166/1261 [03:19<21:10,  1.16s/it]
 13%|█▎        | 167/1261 [03:21<21:16,  1.17s/it]
 13%|█▎        | 168/1261 [03:22<21:06,  1.16s/it]
 13%|█▎        | 169/1261 [03:23<21:03,  1.16s/it]
 13%|█▎        | 170/1261 [03:24<20:56,  1.15s/it]
 14%|█▎        | 171/1261 [03:25<20:50,  1.15s/it]
 14%|█▎        | 172/1261 [03:26<20:51,  1.15s/it]
 14%|█▎        | 173/1261 [03:27<20:52,  1.15s/it]
 14%|█▍        | 174/1261 [03:29<20:48,  1.15s/it]
 14%|█▍        | 175/1261 [03:30<20:43,  1.15s/it]
 14%|█▍        | 176/1261 [03:31<20:42,  1.15s/it]
 14%|█▍        | 177/1261 [03:32<20:45,  1.15s/it]
 14%|█▍        | 178/1261 [03:33<20:28,  1.13s/it]
 14%|█▍        | 179/1261 [03:34<20:24,  1.13s/it]
 14%|█▍        | 180/1261 [03:35<20:33,  1.14s/it]
 14%|█▍        | 181/1261 [03:36<20:27,  1.14s/it]
 14%|█▍        | 182/1261 [03:38<20:22,  1.13s/it]
 15%|█▍        | 183/1261 [03:39<20:23,  1.14s/it]
 15%|█▍        | 184/1261 [03:40<20:28,  1.14s/it]
 15%|█▍        | 185/1261 [03:41<20:29,  1.14s/it]
 15%|█▍        | 186/1261 [03:42<20:23,  1.14s/it]
 15%|█▍        | 187/1261 [03:43<20:18,  1.13s/it]
 15%|█▍        | 188/1261 [03:45<20:37,  1.15s/it]
 15%|█▍        | 189/1261 [03:46<21:40,  1.21s/it]
 15%|█▌        | 190/1261 [03:47<21:46,  1.22s/it]
 15%|█▌        | 191/1261 [03:48<22:00,  1.23s/it]
 15%|█▌        | 192/1261 [03:50<21:37,  1.21s/it]
 15%|█▌        | 193/1261 [03:51<21:23,  1.20s/it]
 15%|█▌        | 194/1261 [03:52<21:02,  1.18s/it]
 15%|█▌        | 195/1261 [03:53<20:53,  1.18s/it]
 16%|█▌        | 196/1261 [03:54<20:51,  1.17s/it]
 16%|█▌        | 197/1261 [03:55<20:48,  1.17s/it]
 16%|█▌        | 198/1261 [03:57<20:46,  1.17s/it]
 16%|█▌        | 199/1261 [03:58<21:32,  1.22s/it]
 16%|█▌        | 200/1261 [03:59<21:18,  1.21s/it]
 16%|█▌        | 201/1261 [04:00<21:31,  1.22s/it]
 16%|█▌        | 202/1261 [04:02<22:33,  1.28s/it]
 16%|█▌        | 203/1261 [04:03<22:26,  1.27s/it]
 16%|█▌        | 204/1261 [04:04<21:53,  1.24s/it]
 16%|█▋        | 205/1261 [04:05<21:51,  1.24s/it]
 16%|█▋        | 206/1261 [04:07<22:16,  1.27s/it]
 16%|█▋        | 207/1261 [04:08<22:14,  1.27s/it]
 16%|█▋        | 208/1261 [04:09<22:46,  1.30s/it]
 17%|█▋        | 209/1261 [04:11<22:35,  1.29s/it]
 17%|█▋        | 210/1261 [04:12<21:49,  1.25s/it]
 17%|█▋        | 211/1261 [04:13<21:43,  1.24s/it]
 17%|█▋        | 212/1261 [04:14<21:53,  1.25s/it]
 17%|█▋        | 213/1261 [04:15<21:50,  1.25s/it]
 17%|█▋        | 214/1261 [04:17<21:45,  1.25s/it]
 17%|█▋        | 215/1261 [04:18<21:46,  1.25s/it]
 17%|█▋        | 216/1261 [04:19<21:46,  1.25s/it]
 17%|█▋        | 217/1261 [04:20<21:14,  1.22s/it]
 17%|█▋        | 218/1261 [04:22<21:23,  1.23s/it]
 17%|█▋        | 219/1261 [04:23<21:54,  1.26s/it]
 17%|█▋        | 220/1261 [04:24<21:19,  1.23s/it]
 18%|█▊        | 221/1261 [04:25<21:02,  1.21s/it]
 18%|█▊        | 222/1261 [04:26<20:41,  1.19s/it]
 18%|█▊        | 223/1261 [04:28<20:29,  1.18s/it]
 18%|█▊        | 224/1261 [04:29<20:20,  1.18s/it]
 18%|█▊        | 225/1261 [04:30<20:14,  1.17s/it]
 18%|█▊        | 226/1261 [04:31<20:08,  1.17s/it]
 18%|█▊        | 227/1261 [04:32<20:01,  1.16s/it]
 18%|█▊        | 228/1261 [04:33<20:00,  1.16s/it]
 18%|█▊        | 229/1261 [04:35<20:01,  1.16s/it]
 18%|█▊        | 230/1261 [04:36<19:51,  1.16s/it]
 18%|█▊        | 231/1261 [04:37<19:53,  1.16s/it]
 18%|█▊        | 232/1261 [04:38<19:58,  1.16s/it]
 18%|█▊        | 233/1261 [04:39<19:52,  1.16s/it]
 19%|█▊        | 234/1261 [04:40<20:02,  1.17s/it]
 19%|█▊        | 235/1261 [04:42<19:56,  1.17s/it]
 19%|█▊        | 236/1261 [04:43<19:51,  1.16s/it]
 19%|█▉        | 237/1261 [04:44<19:50,  1.16s/it]
 19%|█▉        | 238/1261 [04:45<19:51,  1.17s/it]
 19%|█▉        | 239/1261 [04:46<19:46,  1.16s/it]
 19%|█▉        | 240/1261 [04:47<19:47,  1.16s/it]
 19%|█▉        | 241/1261 [04:48<19:40,  1.16s/it]
 19%|█▉        | 242/1261 [04:50<19:45,  1.16s/it]
 19%|█▉        | 243/1261 [04:51<19:41,  1.16s/it]
 19%|█▉        | 244/1261 [04:52<19:39,  1.16s/it]
 19%|█▉        | 245/1261 [04:53<19:34,  1.16s/it]
 20%|█▉        | 246/1261 [04:54<19:28,  1.15s/it]
 20%|█▉        | 247/1261 [04:55<19:24,  1.15s/it]
 20%|█▉        | 248/1261 [04:57<19:23,  1.15s/it]
 20%|█▉        | 249/1261 [04:58<19:15,  1.14s/it]
 20%|█▉        | 250/1261 [04:59<19:42,  1.17s/it]
 20%|█▉        | 251/1261 [05:00<19:57,  1.19s/it]
 20%|█▉        | 252/1261 [05:01<20:40,  1.23s/it]
 20%|██        | 253/1261 [05:03<20:51,  1.24s/it]
 20%|██        | 254/1261 [05:04<20:41,  1.23s/it]
 20%|██        | 255/1261 [05:05<20:27,  1.22s/it]
 20%|██        | 256/1261 [05:06<20:02,  1.20s/it]
 20%|██        | 257/1261 [05:07<19:58,  1.19s/it]
 20%|██        | 258/1261 [05:09<20:22,  1.22s/it]
 21%|██        | 259/1261 [05:10<20:10,  1.21s/it]
 21%|██        | 260/1261 [05:11<20:04,  1.20s/it]
 21%|██        | 261/1261 [05:12<20:19,  1.22s/it]
 21%|██        | 262/1261 [05:14<20:18,  1.22s/it]
 21%|██        | 263/1261 [05:15<20:08,  1.21s/it]
 21%|██        | 264/1261 [05:16<20:08,  1.21s/it]
 21%|██        | 265/1261 [05:17<20:01,  1.21s/it]
 21%|██        | 266/1261 [05:18<19:46,  1.19s/it]
 21%|██        | 267/1261 [05:19<19:22,  1.17s/it]
 21%|██▏       | 268/1261 [05:21<19:36,  1.18s/it]
 21%|██▏       | 269/1261 [05:22<19:22,  1.17s/it]
 21%|██▏       | 270/1261 [05:23<19:07,  1.16s/it]
 21%|██▏       | 271/1261 [05:24<19:00,  1.15s/it]
 22%|██▏       | 272/1261 [05:25<19:06,  1.16s/it]
 22%|██▏       | 273/1261 [05:26<19:08,  1.16s/it]
 22%|██▏       | 274/1261 [05:28<19:04,  1.16s/it]
 22%|██▏       | 275/1261 [05:29<18:56,  1.15s/it]
 22%|██▏       | 276/1261 [05:30<18:56,  1.15s/it]
 22%|██▏       | 277/1261 [05:31<18:56,  1.15s/it]
 22%|██▏       | 278/1261 [05:32<18:48,  1.15s/it]
 22%|██▏       | 279/1261 [05:33<18:52,  1.15s/it]
 22%|██▏       | 280/1261 [05:35<19:17,  1.18s/it]
 22%|██▏       | 281/1261 [05:36<19:07,  1.17s/it]
 22%|██▏       | 282/1261 [05:37<18:53,  1.16s/it]
 22%|██▏       | 283/1261 [05:38<18:49,  1.15s/it]
 23%|██▎       | 284/1261 [05:39<18:49,  1.16s/it]
 23%|██▎       | 285/1261 [05:40<18:42,  1.15s/it]
 23%|██▎       | 286/1261 [05:41<18:35,  1.14s/it]
 23%|██▎       | 287/1261 [05:43<18:42,  1.15s/it]
 23%|██▎       | 288/1261 [05:44<18:39,  1.15s/it]
 23%|██▎       | 289/1261 [05:45<18:32,  1.14s/it]
 23%|██▎       | 290/1261 [05:46<18:39,  1.15s/it]
 23%|██▎       | 291/1261 [05:47<18:34,  1.15s/it]
 23%|██▎       | 292/1261 [05:48<18:58,  1.17s/it]
 23%|██▎       | 293/1261 [05:50<19:09,  1.19s/it]
 23%|██▎       | 294/1261 [05:51<19:00,  1.18s/it]
 23%|██▎       | 295/1261 [05:52<18:48,  1.17s/it]
 23%|██▎       | 296/1261 [05:53<18:39,  1.16s/it]
 24%|██▎       | 297/1261 [05:54<18:30,  1.15s/it]
 24%|██▎       | 298/1261 [05:55<18:29,  1.15s/it]
 24%|██▎       | 299/1261 [05:57<18:25,  1.15s/it]
 24%|██▍       | 300/1261 [05:58<18:18,  1.14s/it]
 24%|██▍       | 301/1261 [05:59<18:20,  1.15s/it]
 24%|██▍       | 302/1261 [06:00<18:30,  1.16s/it]
 24%|██▍       | 303/1261 [06:01<18:42,  1.17s/it]
 24%|██▍       | 304/1261 [06:02<18:31,  1.16s/it]
 24%|██▍       | 305/1261 [06:03<18:21,  1.15s/it]
 24%|██▍       | 306/1261 [06:05<18:15,  1.15s/it]
 24%|██▍       | 307/1261 [06:06<18:11,  1.14s/it]
 24%|██▍       | 308/1261 [06:07<18:15,  1.15s/it]
 25%|██▍       | 309/1261 [06:08<18:08,  1.14s/it]
 25%|██▍       | 310/1261 [06:09<18:12,  1.15s/it]
 25%|██▍       | 311/1261 [06:10<18:15,  1.15s/it]
 25%|██▍       | 312/1261 [06:12<18:19,  1.16s/it]
 25%|██▍       | 313/1261 [06:13<18:13,  1.15s/it]
 25%|██▍       | 314/1261 [06:14<18:14,  1.16s/it]
 25%|██▍       | 315/1261 [06:15<18:08,  1.15s/it]
 25%|██▌       | 316/1261 [06:16<18:04,  1.15s/it]
 25%|██▌       | 317/1261 [06:17<17:56,  1.14s/it]
 25%|██▌       | 318/1261 [06:18<17:56,  1.14s/it]
 25%|██▌       | 319/1261 [06:20<18:01,  1.15s/it]
 25%|██▌       | 320/1261 [06:21<17:56,  1.14s/it]
 25%|██▌       | 321/1261 [06:22<17:54,  1.14s/it]
 26%|██▌       | 322/1261 [06:23<18:00,  1.15s/it]
 26%|██▌       | 323/1261 [06:24<17:55,  1.15s/it]
 26%|██▌       | 324/1261 [06:25<17:49,  1.14s/it]
 26%|██▌       | 325/1261 [06:26<17:59,  1.15s/it]
 26%|██▌       | 326/1261 [06:28<18:18,  1.17s/it]
 26%|██▌       | 327/1261 [06:29<18:44,  1.20s/it]
 26%|██▌       | 328/1261 [06:30<18:26,  1.19s/it]
 26%|██▌       | 329/1261 [06:31<18:12,  1.17s/it]
 26%|██▌       | 330/1261 [06:32<18:06,  1.17s/it]
 26%|██▌       | 331/1261 [06:34<18:01,  1.16s/it]
 26%|██▋       | 332/1261 [06:35<18:24,  1.19s/it]
 26%|██▋       | 333/1261 [06:36<18:31,  1.20s/it]
 26%|██▋       | 334/1261 [06:37<18:35,  1.20s/it]
 27%|██▋       | 335/1261 [06:38<18:39,  1.21s/it]
 27%|██▋       | 336/1261 [06:40<18:41,  1.21s/it]
 27%|██▋       | 337/1261 [06:41<18:59,  1.23s/it]
 27%|██▋       | 338/1261 [06:42<19:18,  1.25s/it]
 27%|██▋       | 339/1261 [06:43<19:07,  1.24s/it]
 27%|██▋       | 340/1261 [06:45<19:25,  1.27s/it]
 27%|██▋       | 341/1261 [06:46<19:32,  1.27s/it]
 27%|██▋       | 342/1261 [06:47<19:39,  1.28s/it]
 27%|██▋       | 343/1261 [06:49<19:44,  1.29s/it]
 27%|██▋       | 344/1261 [06:50<19:41,  1.29s/it]
 27%|██▋       | 345/1261 [06:51<19:17,  1.26s/it]
 27%|██▋       | 346/1261 [06:52<19:24,  1.27s/it]
 28%|██▊       | 347/1261 [06:54<19:22,  1.27s/it]
 28%|██▊       | 348/1261 [06:55<19:26,  1.28s/it]
 28%|██▊       | 349/1261 [06:56<19:18,  1.27s/it]
 28%|██▊       | 350/1261 [06:57<18:47,  1.24s/it]
 28%|██▊       | 351/1261 [06:59<18:15,  1.20s/it]
 28%|██▊       | 352/1261 [07:00<17:53,  1.18s/it]
 28%|██▊       | 353/1261 [07:01<17:45,  1.17s/it]
 28%|██▊       | 354/1261 [07:02<18:00,  1.19s/it]
 28%|██▊       | 355/1261 [07:03<18:34,  1.23s/it]
 28%|██▊       | 356/1261 [07:05<18:55,  1.26s/it]
 28%|██▊       | 357/1261 [07:06<19:22,  1.29s/it]
 28%|██▊       | 358/1261 [07:08<20:12,  1.34s/it]
 28%|██▊       | 359/1261 [07:09<19:50,  1.32s/it]
 29%|██▊       | 360/1261 [07:10<19:45,  1.32s/it]
 29%|██▊       | 361/1261 [07:12<20:08,  1.34s/it]
 29%|██▊       | 362/1261 [07:13<20:15,  1.35s/it]
 29%|██▉       | 363/1261 [07:14<19:31,  1.30s/it]
 29%|██▉       | 364/1261 [07:15<19:15,  1.29s/it]
 29%|██▉       | 365/1261 [07:17<18:41,  1.25s/it]
 29%|██▉       | 366/1261 [07:18<18:20,  1.23s/it]
 29%|██▉       | 367/1261 [07:19<18:16,  1.23s/it]
 29%|██▉       | 368/1261 [07:20<18:03,  1.21s/it]
 29%|██▉       | 369/1261 [07:21<17:47,  1.20s/it]
 29%|██▉       | 370/1261 [07:22<17:33,  1.18s/it]
 29%|██▉       | 371/1261 [07:24<17:22,  1.17s/it]
 30%|██▉       | 372/1261 [07:25<17:20,  1.17s/it]
 30%|██▉       | 373/1261 [07:26<17:17,  1.17s/it]
 30%|██▉       | 374/1261 [07:27<17:16,  1.17s/it]
 30%|██▉       | 375/1261 [07:28<17:13,  1.17s/it]
 30%|██▉       | 376/1261 [07:29<17:08,  1.16s/it]
 30%|██▉       | 377/1261 [07:31<17:03,  1.16s/it]
 30%|██▉       | 378/1261 [07:32<17:07,  1.16s/it]
 30%|███       | 379/1261 [07:33<17:02,  1.16s/it]
 30%|███       | 380/1261 [07:34<17:01,  1.16s/it]
 30%|███       | 381/1261 [07:35<16:59,  1.16s/it]
 30%|███       | 382/1261 [07:36<16:59,  1.16s/it]
 30%|███       | 383/1261 [07:37<16:52,  1.15s/it]
 30%|███       | 384/1261 [07:39<16:50,  1.15s/it]
 31%|███       | 385/1261 [07:40<16:41,  1.14s/it]
 31%|███       | 386/1261 [07:41<16:48,  1.15s/it]
 31%|███       | 387/1261 [07:42<17:00,  1.17s/it]
 31%|███       | 388/1261 [07:43<17:46,  1.22s/it]
 31%|███       | 389/1261 [07:45<18:11,  1.25s/it]
 31%|███       | 390/1261 [07:46<18:31,  1.28s/it]
 31%|███       | 391/1261 [07:47<18:40,  1.29s/it]
 31%|███       | 392/1261 [07:49<18:10,  1.25s/it]
 31%|███       | 393/1261 [07:50<17:43,  1.22s/it]
 31%|███       | 394/1261 [07:51<17:24,  1.21s/it]
 31%|███▏      | 395/1261 [07:52<17:09,  1.19s/it]
 31%|███▏      | 396/1261 [07:53<16:58,  1.18s/it]
 31%|███▏      | 397/1261 [07:54<17:03,  1.18s/it]
 32%|███▏      | 398/1261 [07:56<16:50,  1.17s/it]
 32%|███▏      | 399/1261 [07:57<16:46,  1.17s/it]
 32%|███▏      | 400/1261 [07:58<16:44,  1.17s/it]
 32%|███▏      | 401/1261 [07:59<16:38,  1.16s/it]
 32%|███▏      | 402/1261 [08:00<16:38,  1.16s/it]
 32%|███▏      | 403/1261 [08:01<16:27,  1.15s/it]
 32%|███▏      | 404/1261 [08:02<16:22,  1.15s/it]
 32%|███▏      | 405/1261 [08:04<16:18,  1.14s/it]
 32%|███▏      | 406/1261 [08:05<16:17,  1.14s/it]
 32%|███▏      | 407/1261 [08:06<16:19,  1.15s/it]
 32%|███▏      | 408/1261 [08:07<16:15,  1.14s/it]
 32%|███▏      | 409/1261 [08:08<16:14,  1.14s/it]
 33%|███▎      | 410/1261 [08:09<16:15,  1.15s/it]
 33%|███▎      | 411/1261 [08:10<16:19,  1.15s/it]
 33%|███▎      | 412/1261 [08:12<16:23,  1.16s/it]
 33%|███▎      | 413/1261 [08:13<16:19,  1.16s/it]
 33%|███▎      | 414/1261 [08:14<16:17,  1.15s/it]
 33%|███▎      | 415/1261 [08:15<16:14,  1.15s/it]
 33%|███▎      | 416/1261 [08:16<16:16,  1.16s/it]
 33%|███▎      | 417/1261 [08:17<16:11,  1.15s/it]
 33%|███▎      | 418/1261 [08:19<16:11,  1.15s/it]
 33%|███▎      | 419/1261 [08:20<16:12,  1.15s/it]
 33%|███▎      | 420/1261 [08:21<16:13,  1.16s/it]
 33%|███▎      | 421/1261 [08:22<16:47,  1.20s/it]
 33%|███▎      | 422/1261 [08:24<17:40,  1.26s/it]
 34%|███▎      | 423/1261 [08:25<17:45,  1.27s/it]
 34%|███▎      | 424/1261 [08:26<17:46,  1.27s/it]
 34%|███▎      | 425/1261 [08:27<17:47,  1.28s/it]
 34%|███▍      | 426/1261 [08:29<17:46,  1.28s/it]
 34%|███▍      | 427/1261 [08:30<17:49,  1.28s/it]
 34%|███▍      | 428/1261 [08:31<18:04,  1.30s/it]
 34%|███▍      | 429/1261 [08:33<17:47,  1.28s/it]
 34%|███▍      | 430/1261 [08:34<17:10,  1.24s/it]
 34%|███▍      | 431/1261 [08:35<16:47,  1.21s/it]
 34%|███▍      | 432/1261 [08:36<16:28,  1.19s/it]
 34%|███▍      | 433/1261 [08:37<16:16,  1.18s/it]
 34%|███▍      | 434/1261 [08:38<16:10,  1.17s/it]
 34%|███▍      | 435/1261 [08:40<16:00,  1.16s/it]
 35%|███▍      | 436/1261 [08:41<15:56,  1.16s/it]
 35%|███▍      | 437/1261 [08:42<15:55,  1.16s/it]
 35%|███▍      | 438/1261 [08:43<15:52,  1.16s/it]
 35%|███▍      | 439/1261 [08:44<15:50,  1.16s/it]
 35%|███▍      | 440/1261 [08:45<15:46,  1.15s/it]
 35%|███▍      | 441/1261 [08:46<15:48,  1.16s/it]
 35%|███▌      | 442/1261 [08:48<15:46,  1.16s/it]
 35%|███▌      | 443/1261 [08:49<15:44,  1.15s/it]
 35%|███▌      | 444/1261 [08:50<16:07,  1.18s/it]
 35%|███▌      | 445/1261 [08:51<16:11,  1.19s/it]
 35%|███▌      | 446/1261 [08:52<16:05,  1.19s/it]
 35%|███▌      | 447/1261 [08:54<15:52,  1.17s/it]
 36%|███▌      | 448/1261 [08:55<15:50,  1.17s/it]
 36%|███▌      | 449/1261 [08:56<15:43,  1.16s/it]
 36%|███▌      | 450/1261 [08:57<15:46,  1.17s/it]
 36%|███▌      | 451/1261 [08:58<15:46,  1.17s/it]
 36%|███▌      | 452/1261 [08:59<16:05,  1.19s/it]
 36%|███▌      | 453/1261 [09:01<15:52,  1.18s/it]
 36%|███▌      | 454/1261 [09:02<15:45,  1.17s/it]
 36%|███▌      | 455/1261 [09:03<15:38,  1.16s/it]
 36%|███▌      | 456/1261 [09:04<15:32,  1.16s/it]
 36%|███▌      | 457/1261 [09:05<15:23,  1.15s/it]
 36%|███▋      | 458/1261 [09:06<15:44,  1.18s/it]
 36%|███▋      | 459/1261 [09:08<15:36,  1.17s/it]
 36%|███▋      | 460/1261 [09:09<15:27,  1.16s/it]
 37%|███▋      | 461/1261 [09:10<15:23,  1.15s/it]
 37%|███▋      | 462/1261 [09:11<15:25,  1.16s/it]
 37%|███▋      | 463/1261 [09:12<15:45,  1.18s/it]
 37%|███▋      | 464/1261 [09:13<15:41,  1.18s/it]
 37%|███▋      | 465/1261 [09:15<15:43,  1.19s/it]
 37%|███▋      | 466/1261 [09:16<15:43,  1.19s/it]
 37%|███▋      | 467/1261 [09:17<15:26,  1.17s/it]
 37%|███▋      | 468/1261 [09:18<15:24,  1.17s/it]
 37%|███▋      | 469/1261 [09:19<15:42,  1.19s/it]
 37%|███▋      | 470/1261 [09:21<16:04,  1.22s/it]
 37%|███▋      | 471/1261 [09:22<15:43,  1.19s/it]
 37%|███▋      | 472/1261 [09:23<15:31,  1.18s/it]
 38%|███▊      | 473/1261 [09:24<15:20,  1.17s/it]
 38%|███▊      | 474/1261 [09:25<15:11,  1.16s/it]
 38%|███▊      | 475/1261 [09:26<15:17,  1.17s/it]
 38%|███▊      | 476/1261 [09:28<15:15,  1.17s/it]
 38%|███▊      | 477/1261 [09:29<15:07,  1.16s/it]
 38%|███▊      | 478/1261 [09:30<15:05,  1.16s/it]
 38%|███▊      | 479/1261 [09:31<14:56,  1.15s/it]
 38%|███▊      | 480/1261 [09:32<14:52,  1.14s/it]
 38%|███▊      | 481/1261 [09:33<14:49,  1.14s/it]
 38%|███▊      | 482/1261 [09:34<14:53,  1.15s/it]
 38%|███▊      | 483/1261 [09:36<14:52,  1.15s/it]
 38%|███▊      | 484/1261 [09:37<14:58,  1.16s/it]
 38%|███▊      | 485/1261 [09:38<14:54,  1.15s/it]
 39%|███▊      | 486/1261 [09:39<15:00,  1.16s/it]
 39%|███▊      | 487/1261 [09:40<14:57,  1.16s/it]
 39%|███▊      | 488/1261 [09:41<14:51,  1.15s/it]
 39%|███▉      | 489/1261 [09:42<14:47,  1.15s/it]
 39%|███▉      | 490/1261 [09:44<14:52,  1.16s/it]
 39%|███▉      | 491/1261 [09:45<14:53,  1.16s/it]
 39%|███▉      | 492/1261 [09:46<14:48,  1.16s/it]
 39%|███▉      | 493/1261 [09:47<14:43,  1.15s/it]
 39%|███▉      | 494/1261 [09:48<14:55,  1.17s/it]
 39%|███▉      | 495/1261 [09:49<14:49,  1.16s/it]
 39%|███▉      | 496/1261 [09:51<14:42,  1.15s/it]
 39%|███▉      | 497/1261 [09:52<14:40,  1.15s/it]
 39%|███▉      | 498/1261 [09:53<14:36,  1.15s/it]
 40%|███▉      | 499/1261 [09:54<14:48,  1.17s/it]
 40%|███▉      | 500/1261 [09:55<14:43,  1.16s/it]
 40%|███▉      | 501/1261 [09:56<14:39,  1.16s/it]
 40%|███▉      | 502/1261 [09:58<14:40,  1.16s/it]
 40%|███▉      | 503/1261 [09:59<14:34,  1.15s/it]
 40%|███▉      | 504/1261 [10:00<14:27,  1.15s/it]
 40%|████      | 505/1261 [10:01<14:25,  1.14s/it]
 40%|████      | 506/1261 [10:02<14:25,  1.15s/it]
 40%|████      | 507/1261 [10:03<14:30,  1.15s/it]
 40%|████      | 508/1261 [10:04<14:24,  1.15s/it]
 40%|████      | 509/1261 [10:06<14:23,  1.15s/it]
 40%|████      | 510/1261 [10:07<14:24,  1.15s/it]
 41%|████      | 511/1261 [10:08<14:36,  1.17s/it]
 41%|████      | 512/1261 [10:09<14:56,  1.20s/it]
 41%|████      | 513/1261 [10:10<15:07,  1.21s/it]
 41%|████      | 514/1261 [10:12<14:54,  1.20s/it]
 41%|████      | 515/1261 [10:13<14:46,  1.19s/it]
 41%|████      | 516/1261 [10:14<14:37,  1.18s/it]
 41%|████      | 517/1261 [10:15<14:29,  1.17s/it]
 41%|████      | 518/1261 [10:16<14:30,  1.17s/it]
 41%|████      | 519/1261 [10:17<14:29,  1.17s/it]
 41%|████      | 520/1261 [10:19<14:20,  1.16s/it]
 41%|████▏     | 521/1261 [10:20<14:21,  1.16s/it]
 41%|████▏     | 522/1261 [10:21<14:40,  1.19s/it]
 41%|████▏     | 523/1261 [10:22<14:34,  1.18s/it]
 42%|████▏     | 524/1261 [10:23<14:25,  1.17s/it]
 42%|████▏     | 525/1261 [10:24<14:21,  1.17s/it]
 42%|████▏     | 526/1261 [10:26<14:23,  1.17s/it]
 42%|████▏     | 527/1261 [10:27<14:17,  1.17s/it]
 42%|████▏     | 528/1261 [10:28<14:09,  1.16s/it]
 42%|████▏     | 529/1261 [10:29<14:05,  1.15s/it]
 42%|████▏     | 530/1261 [10:30<14:05,  1.16s/it]
 42%|████▏     | 531/1261 [10:31<14:03,  1.16s/it]
 42%|████▏     | 532/1261 [10:33<13:59,  1.15s/it]
 42%|████▏     | 533/1261 [10:34<14:02,  1.16s/it]
 42%|████▏     | 534/1261 [10:35<14:03,  1.16s/it]
 42%|████▏     | 535/1261 [10:36<13:56,  1.15s/it]
 43%|████▎     | 536/1261 [10:37<13:56,  1.15s/it]
 43%|████▎     | 537/1261 [10:38<13:55,  1.15s/it]
 43%|████▎     | 538/1261 [10:39<13:57,  1.16s/it]
 43%|████▎     | 539/1261 [10:41<13:54,  1.16s/it]
 43%|████▎     | 540/1261 [10:42<13:49,  1.15s/it]
 43%|████▎     | 541/1261 [10:43<13:52,  1.16s/it]
 43%|████▎     | 542/1261 [10:44<13:51,  1.16s/it]
 43%|████▎     | 543/1261 [10:45<13:58,  1.17s/it]
 43%|████▎     | 544/1261 [10:46<13:47,  1.15s/it]
 43%|████▎     | 545/1261 [10:48<13:42,  1.15s/it]
 43%|████▎     | 546/1261 [10:49<13:44,  1.15s/it]
 43%|████▎     | 547/1261 [10:50<13:40,  1.15s/it]
 43%|████▎     | 548/1261 [10:51<13:42,  1.15s/it]
 44%|████▎     | 549/1261 [10:52<13:38,  1.15s/it]
 44%|████▎     | 550/1261 [10:53<13:38,  1.15s/it]
 44%|████▎     | 551/1261 [10:54<13:34,  1.15s/it]
 44%|████▍     | 552/1261 [10:56<13:40,  1.16s/it]
 44%|████▍     | 553/1261 [10:57<13:37,  1.15s/it]
 44%|████▍     | 554/1261 [10:58<13:32,  1.15s/it]
 44%|████▍     | 555/1261 [10:59<13:30,  1.15s/it]
 44%|████▍     | 556/1261 [11:00<13:32,  1.15s/it]
 44%|████▍     | 557/1261 [11:01<13:26,  1.15s/it]
 44%|████▍     | 558/1261 [11:02<13:24,  1.14s/it]
 44%|████▍     | 559/1261 [11:04<13:18,  1.14s/it]
 44%|████▍     | 560/1261 [11:05<13:21,  1.14s/it]
 44%|████▍     | 561/1261 [11:06<13:23,  1.15s/it]
 45%|████▍     | 562/1261 [11:07<13:23,  1.15s/it]
 45%|████▍     | 563/1261 [11:08<13:23,  1.15s/it]
 45%|████▍     | 564/1261 [11:09<13:20,  1.15s/it]
 45%|████▍     | 565/1261 [11:10<13:15,  1.14s/it]
 45%|████▍     | 566/1261 [11:12<13:28,  1.16s/it]
 45%|████▍     | 567/1261 [11:13<13:43,  1.19s/it]
 45%|████▌     | 568/1261 [11:14<13:38,  1.18s/it]
 45%|████▌     | 569/1261 [11:15<13:34,  1.18s/it]
 45%|████▌     | 570/1261 [11:16<13:23,  1.16s/it]
 45%|████▌     | 571/1261 [11:18<13:14,  1.15s/it]
 45%|████▌     | 572/1261 [11:19<13:11,  1.15s/it]
 45%|████▌     | 573/1261 [11:20<13:06,  1.14s/it]
 46%|████▌     | 574/1261 [11:21<13:09,  1.15s/it]
 46%|████▌     | 575/1261 [11:22<13:02,  1.14s/it]
 46%|████▌     | 576/1261 [11:23<13:05,  1.15s/it]
 46%|████▌     | 577/1261 [11:24<13:03,  1.15s/it]
 46%|████▌     | 578/1261 [11:26<13:00,  1.14s/it]
 46%|████▌     | 579/1261 [11:27<13:01,  1.15s/it]
 46%|████▌     | 580/1261 [11:28<13:00,  1.15s/it]
 46%|████▌     | 581/1261 [11:29<12:53,  1.14s/it]
 46%|████▌     | 582/1261 [11:30<13:12,  1.17s/it]
 46%|████▌     | 583/1261 [11:31<13:02,  1.15s/it]
 46%|████▋     | 584/1261 [11:32<13:01,  1.15s/it]
 46%|████▋     | 585/1261 [11:34<12:55,  1.15s/it]
 46%|████▋     | 586/1261 [11:35<12:47,  1.14s/it]
 47%|████▋     | 587/1261 [11:36<12:42,  1.13s/it]
 47%|████▋     | 588/1261 [11:37<12:40,  1.13s/it]
 47%|████▋     | 589/1261 [11:38<12:40,  1.13s/it]
 47%|████▋     | 590/1261 [11:39<12:34,  1.12s/it]
 47%|████▋     | 591/1261 [11:40<12:33,  1.12s/it]
 47%|████▋     | 592/1261 [11:41<12:37,  1.13s/it]
 47%|████▋     | 593/1261 [11:43<12:37,  1.13s/it]
 47%|████▋     | 594/1261 [11:44<12:33,  1.13s/it]
 47%|████▋     | 595/1261 [11:45<12:31,  1.13s/it]
 47%|████▋     | 596/1261 [11:46<12:34,  1.14s/it]
 47%|████▋     | 597/1261 [11:47<12:33,  1.13s/it]
 47%|████▋     | 598/1261 [11:48<12:29,  1.13s/it]
 48%|████▊     | 599/1261 [11:49<12:25,  1.13s/it]
 48%|████▊     | 600/1261 [11:51<12:24,  1.13s/it]
 48%|████▊     | 601/1261 [11:52<12:25,  1.13s/it]
 48%|████▊     | 602/1261 [11:53<12:26,  1.13s/it]
 48%|████▊     | 603/1261 [11:54<12:25,  1.13s/it]
 48%|████▊     | 604/1261 [11:55<12:26,  1.14s/it]
 48%|████▊     | 605/1261 [11:56<12:25,  1.14s/it]
 48%|████▊     | 606/1261 [11:57<12:21,  1.13s/it]
 48%|████▊     | 607/1261 [11:58<12:21,  1.13s/it]
 48%|████▊     | 608/1261 [12:00<12:23,  1.14s/it]
 48%|████▊     | 609/1261 [12:01<12:22,  1.14s/it]
 48%|████▊     | 610/1261 [12:02<12:22,  1.14s/it]
 48%|████▊     | 611/1261 [12:03<12:19,  1.14s/it]
 49%|████▊     | 612/1261 [12:04<12:26,  1.15s/it]
 49%|████▊     | 613/1261 [12:05<12:18,  1.14s/it]
 49%|████▊     | 614/1261 [12:06<12:16,  1.14s/it]
 49%|████▉     | 615/1261 [12:08<12:15,  1.14s/it]
 49%|████▉     | 616/1261 [12:09<12:13,  1.14s/it]
 49%|████▉     | 617/1261 [12:10<12:11,  1.14s/it]
 49%|████▉     | 618/1261 [12:11<12:11,  1.14s/it]
 49%|████▉     | 619/1261 [12:12<12:09,  1.14s/it]
 49%|████▉     | 620/1261 [12:13<12:08,  1.14s/it]
 49%|████▉     | 621/1261 [12:14<12:21,  1.16s/it]
 49%|████▉     | 622/1261 [12:16<12:44,  1.20s/it]
 49%|████▉     | 623/1261 [12:17<12:59,  1.22s/it]
 49%|████▉     | 624/1261 [12:18<13:13,  1.25s/it]
 50%|████▉     | 625/1261 [12:20<13:24,  1.27s/it]
 50%|████▉     | 626/1261 [12:21<13:30,  1.28s/it]
 50%|████▉     | 627/1261 [12:22<13:14,  1.25s/it]
 50%|████▉     | 628/1261 [12:23<12:52,  1.22s/it]
 50%|████▉     | 629/1261 [12:24<12:34,  1.19s/it]
 50%|████▉     | 630/1261 [12:26<12:22,  1.18s/it]
 50%|█████     | 631/1261 [12:27<12:18,  1.17s/it]
 50%|█████     | 632/1261 [12:28<12:18,  1.17s/it]
 50%|█████     | 633/1261 [12:29<12:32,  1.20s/it]
 50%|█████     | 634/1261 [12:30<12:23,  1.19s/it]
 50%|█████     | 635/1261 [12:31<12:17,  1.18s/it]
 50%|█████     | 636/1261 [12:33<12:12,  1.17s/it]
 51%|█████     | 637/1261 [12:34<12:11,  1.17s/it]
 51%|█████     | 638/1261 [12:35<12:02,  1.16s/it]
 51%|█████     | 639/1261 [12:36<11:55,  1.15s/it]
 51%|█████     | 640/1261 [12:37<11:58,  1.16s/it]
 51%|█████     | 641/1261 [12:38<11:56,  1.16s/it]
 51%|█████     | 642/1261 [12:40<11:52,  1.15s/it]
 51%|█████     | 643/1261 [12:41<11:57,  1.16s/it]
 51%|█████     | 644/1261 [12:42<11:55,  1.16s/it]
 51%|█████     | 645/1261 [12:43<11:51,  1.16s/it]
 51%|█████     | 646/1261 [12:44<11:46,  1.15s/it]
 51%|█████▏    | 647/1261 [12:45<11:41,  1.14s/it]
 51%|█████▏    | 648/1261 [12:46<11:42,  1.15s/it]
 51%|█████▏    | 649/1261 [12:48<11:40,  1.15s/it]
 52%|█████▏    | 650/1261 [12:49<11:36,  1.14s/it]
 52%|█████▏    | 651/1261 [12:50<11:34,  1.14s/it]
 52%|█████▏    | 652/1261 [12:51<11:36,  1.14s/it]
 52%|█████▏    | 653/1261 [12:52<11:34,  1.14s/it]
 52%|█████▏    | 654/1261 [12:53<11:37,  1.15s/it]
 52%|█████▏    | 655/1261 [12:54<11:36,  1.15s/it]
 52%|█████▏    | 656/1261 [12:56<11:31,  1.14s/it]
 52%|█████▏    | 657/1261 [12:57<11:24,  1.13s/it]
 52%|█████▏    | 658/1261 [12:58<11:23,  1.13s/it]
 52%|█████▏    | 659/1261 [12:59<11:22,  1.13s/it]
 52%|█████▏    | 660/1261 [13:00<11:20,  1.13s/it]
 52%|█████▏    | 661/1261 [13:01<11:21,  1.14s/it]
 52%|█████▏    | 662/1261 [13:02<11:19,  1.13s/it]
 53%|█████▎    | 663/1261 [13:04<11:20,  1.14s/it]
 53%|█████▎    | 664/1261 [13:05<11:19,  1.14s/it]
 53%|█████▎    | 665/1261 [13:06<11:19,  1.14s/it]
 53%|█████▎    | 666/1261 [13:07<11:19,  1.14s/it]
 53%|█████▎    | 667/1261 [13:08<11:13,  1.13s/it]
 53%|█████▎    | 668/1261 [13:09<11:12,  1.13s/it]
 53%|█████▎    | 669/1261 [13:10<11:09,  1.13s/it]
 53%|█████▎    | 670/1261 [13:12<11:23,  1.16s/it]
 53%|█████▎    | 671/1261 [13:13<11:39,  1.19s/it]
 53%|█████▎    | 672/1261 [13:14<11:47,  1.20s/it]
 53%|█████▎    | 673/1261 [13:15<11:42,  1.20s/it]
 53%|█████▎    | 674/1261 [13:16<11:34,  1.18s/it]
 54%|█████▎    | 675/1261 [13:17<11:25,  1.17s/it]
 54%|█████▎    | 676/1261 [13:19<11:19,  1.16s/it]
 54%|█████▎    | 677/1261 [13:20<11:20,  1.16s/it]
 54%|█████▍    | 678/1261 [13:21<11:16,  1.16s/it]
 54%|█████▍    | 679/1261 [13:22<11:08,  1.15s/it]
 54%|█████▍    | 680/1261 [13:23<11:09,  1.15s/it]
 54%|█████▍    | 681/1261 [13:24<11:04,  1.15s/it]
 54%|█████▍    | 682/1261 [13:26<11:03,  1.15s/it]
 54%|█████▍    | 683/1261 [13:27<11:00,  1.14s/it]
 54%|█████▍    | 684/1261 [13:28<10:58,  1.14s/it]
 54%|█████▍    | 685/1261 [13:29<10:58,  1.14s/it]
 54%|█████▍    | 686/1261 [13:30<10:54,  1.14s/it]
 54%|█████▍    | 687/1261 [13:31<10:54,  1.14s/it]
 55%|█████▍    | 688/1261 [13:32<10:56,  1.15s/it]
 55%|█████▍    | 689/1261 [13:33<10:51,  1.14s/it]
 55%|█████▍    | 690/1261 [13:35<10:53,  1.14s/it]
 55%|█████▍    | 691/1261 [13:36<10:53,  1.15s/it]
 55%|█████▍    | 692/1261 [13:37<10:53,  1.15s/it]
 55%|█████▍    | 693/1261 [13:38<10:53,  1.15s/it]
 55%|█████▌    | 694/1261 [13:39<10:49,  1.15s/it]
 55%|█████▌    | 695/1261 [13:40<10:45,  1.14s/it]
 55%|█████▌    | 696/1261 [13:42<10:44,  1.14s/it]
 55%|█████▌    | 697/1261 [13:43<10:54,  1.16s/it]
 55%|█████▌    | 698/1261 [13:44<10:58,  1.17s/it]
 55%|█████▌    | 699/1261 [13:45<10:47,  1.15s/it]
 56%|█████▌    | 700/1261 [13:46<10:47,  1.15s/it]
 56%|█████▌    | 701/1261 [13:47<10:42,  1.15s/it]
 56%|█████▌    | 702/1261 [13:48<10:39,  1.14s/it]
 56%|█████▌    | 703/1261 [13:50<10:33,  1.14s/it]
 56%|█████▌    | 704/1261 [13:51<10:35,  1.14s/it]
 56%|█████▌    | 705/1261 [13:52<10:56,  1.18s/it]
 56%|█████▌    | 706/1261 [13:53<11:21,  1.23s/it]
 56%|█████▌    | 707/1261 [13:55<11:11,  1.21s/it]
 56%|█████▌    | 708/1261 [13:56<11:10,  1.21s/it]
 56%|█████▌    | 709/1261 [13:57<11:20,  1.23s/it]
 56%|█████▋    | 710/1261 [13:59<12:08,  1.32s/it]
 56%|█████▋    | 711/1261 [14:00<11:55,  1.30s/it]
 56%|█████▋    | 712/1261 [14:01<11:57,  1.31s/it]
 57%|█████▋    | 713/1261 [14:02<11:54,  1.30s/it]
 57%|█████▋    | 714/1261 [14:04<11:39,  1.28s/it]
 57%|█████▋    | 715/1261 [14:05<11:19,  1.24s/it]
 57%|█████▋    | 716/1261 [14:06<11:30,  1.27s/it]
 57%|█████▋    | 717/1261 [14:08<11:59,  1.32s/it]
 57%|█████▋    | 718/1261 [14:09<12:04,  1.33s/it]
 57%|█████▋    | 719/1261 [14:10<11:32,  1.28s/it]
 57%|█████▋    | 720/1261 [14:11<11:25,  1.27s/it]
 57%|█████▋    | 721/1261 [14:13<11:17,  1.25s/it]
 57%|█████▋    | 722/1261 [14:14<11:05,  1.24s/it]
 57%|█████▋    | 723/1261 [14:15<11:17,  1.26s/it]
 57%|█████▋    | 724/1261 [14:16<11:42,  1.31s/it]
 57%|█████▋    | 725/1261 [14:18<11:17,  1.26s/it]
 58%|█████▊    | 726/1261 [14:19<10:55,  1.23s/it]
 58%|█████▊    | 727/1261 [14:20<10:37,  1.19s/it]
 58%|█████▊    | 728/1261 [14:21<10:25,  1.17s/it]
 58%|█████▊    | 729/1261 [14:22<10:16,  1.16s/it]
 58%|█████▊    | 730/1261 [14:23<10:09,  1.15s/it]
 58%|█████▊    | 731/1261 [14:24<10:05,  1.14s/it]
 58%|█████▊    | 732/1261 [14:25<10:01,  1.14s/it]
 58%|█████▊    | 733/1261 [14:27<10:16,  1.17s/it]
 58%|█████▊    | 734/1261 [14:28<10:15,  1.17s/it]
 58%|█████▊    | 735/1261 [14:29<10:11,  1.16s/it]
 58%|█████▊    | 736/1261 [14:30<10:04,  1.15s/it]
 58%|█████▊    | 737/1261 [14:31<09:57,  1.14s/it]
 59%|█████▊    | 738/1261 [14:33<10:12,  1.17s/it]
 59%|█████▊    | 739/1261 [14:34<10:24,  1.20s/it]
 59%|█████▊    | 740/1261 [14:35<10:18,  1.19s/it]
 59%|█████▉    | 741/1261 [14:36<10:12,  1.18s/it]
 59%|█████▉    | 742/1261 [14:37<10:05,  1.17s/it]
 59%|█████▉    | 743/1261 [14:38<09:59,  1.16s/it]
 59%|█████▉    | 744/1261 [14:40<09:53,  1.15s/it]
 59%|█████▉    | 745/1261 [14:41<09:48,  1.14s/it]
 59%|█████▉    | 746/1261 [14:42<09:46,  1.14s/it]
 59%|█████▉    | 747/1261 [14:43<09:41,  1.13s/it]
 59%|█████▉    | 748/1261 [14:44<09:55,  1.16s/it]
 59%|█████▉    | 749/1261 [14:45<09:48,  1.15s/it]
 59%|█████▉    | 750/1261 [14:46<09:42,  1.14s/it]
 60%|█████▉    | 751/1261 [14:47<09:38,  1.13s/it]
 60%|█████▉    | 752/1261 [14:49<09:37,  1.13s/it]
 60%|█████▉    | 753/1261 [14:50<09:33,  1.13s/it]
 60%|█████▉    | 754/1261 [14:51<09:30,  1.13s/it]
 60%|█████▉    | 755/1261 [14:52<09:30,  1.13s/it]
 60%|█████▉    | 756/1261 [14:53<09:30,  1.13s/it]
 60%|██████    | 757/1261 [14:54<09:28,  1.13s/it]
 60%|██████    | 758/1261 [14:55<09:28,  1.13s/it]
 60%|██████    | 759/1261 [14:57<09:27,  1.13s/it]
 60%|██████    | 760/1261 [14:58<09:25,  1.13s/it]
 60%|██████    | 761/1261 [14:59<09:24,  1.13s/it]
 60%|██████    | 762/1261 [15:00<09:22,  1.13s/it]
 61%|██████    | 763/1261 [15:01<09:22,  1.13s/it]
 61%|██████    | 764/1261 [15:02<09:24,  1.14s/it]
 61%|██████    | 765/1261 [15:03<09:24,  1.14s/it]
 61%|██████    | 766/1261 [15:04<09:22,  1.14s/it]
 61%|██████    | 767/1261 [15:06<09:17,  1.13s/it]
 61%|██████    | 768/1261 [15:07<09:18,  1.13s/it]
 61%|██████    | 769/1261 [15:08<09:19,  1.14s/it]
 61%|██████    | 770/1261 [15:09<09:20,  1.14s/it]
 61%|██████    | 771/1261 [15:10<09:18,  1.14s/it]
 61%|██████    | 772/1261 [15:11<09:18,  1.14s/it]
 61%|██████▏   | 773/1261 [15:13<09:34,  1.18s/it]
 61%|██████▏   | 774/1261 [15:14<09:31,  1.17s/it]
 61%|██████▏   | 775/1261 [15:15<09:34,  1.18s/it]
 62%|██████▏   | 776/1261 [15:16<09:29,  1.17s/it]
 62%|██████▏   | 777/1261 [15:17<09:22,  1.16s/it]
 62%|██████▏   | 778/1261 [15:18<09:18,  1.16s/it]
 62%|██████▏   | 779/1261 [15:19<09:12,  1.15s/it]
 62%|██████▏   | 780/1261 [15:21<09:12,  1.15s/it]
 62%|██████▏   | 781/1261 [15:22<09:07,  1.14s/it]
 62%|██████▏   | 782/1261 [15:23<09:05,  1.14s/it]
 62%|██████▏   | 783/1261 [15:24<09:03,  1.14s/it]
 62%|██████▏   | 784/1261 [15:25<09:03,  1.14s/it]
 62%|██████▏   | 785/1261 [15:26<09:01,  1.14s/it]
 62%|██████▏   | 786/1261 [15:27<08:56,  1.13s/it]
 62%|██████▏   | 787/1261 [15:29<08:55,  1.13s/it]
 62%|██████▏   | 788/1261 [15:30<08:57,  1.14s/it]
 63%|██████▎   | 789/1261 [15:31<08:56,  1.14s/it]
 63%|██████▎   | 790/1261 [15:32<08:53,  1.13s/it]
 63%|██████▎   | 791/1261 [15:33<08:54,  1.14s/it]
 63%|██████▎   | 792/1261 [15:34<08:52,  1.14s/it]
 63%|██████▎   | 793/1261 [15:35<08:54,  1.14s/it]
 63%|██████▎   | 794/1261 [15:37<08:53,  1.14s/it]
 63%|██████▎   | 795/1261 [15:38<08:48,  1.13s/it]
 63%|██████▎   | 796/1261 [15:39<08:47,  1.13s/it]
 63%|██████▎   | 797/1261 [15:40<08:45,  1.13s/it]
 63%|██████▎   | 798/1261 [15:41<08:42,  1.13s/it]
 63%|██████▎   | 799/1261 [15:42<08:46,  1.14s/it]
 63%|██████▎   | 800/1261 [15:43<08:45,  1.14s/it]
 64%|██████▎   | 801/1261 [15:44<08:44,  1.14s/it]
 64%|██████▎   | 802/1261 [15:46<08:40,  1.13s/it]
 64%|██████▎   | 803/1261 [15:47<08:38,  1.13s/it]
 64%|██████▍   | 804/1261 [15:48<08:40,  1.14s/it]
 64%|██████▍   | 805/1261 [15:49<08:37,  1.14s/it]
 64%|██████▍   | 806/1261 [15:50<08:38,  1.14s/it]
 64%|██████▍   | 807/1261 [15:51<08:37,  1.14s/it]
 64%|██████▍   | 808/1261 [15:52<08:36,  1.14s/it]
 64%|██████▍   | 809/1261 [15:54<08:32,  1.13s/it]
 64%|██████▍   | 810/1261 [15:55<08:37,  1.15s/it]
 64%|██████▍   | 811/1261 [15:56<08:35,  1.15s/it]
 64%|██████▍   | 812/1261 [15:57<08:32,  1.14s/it]
 64%|██████▍   | 813/1261 [15:58<08:29,  1.14s/it]
 65%|██████▍   | 814/1261 [15:59<08:28,  1.14s/it]
 65%|██████▍   | 815/1261 [16:00<08:27,  1.14s/it]
 65%|██████▍   | 816/1261 [16:02<08:24,  1.13s/it]
 65%|██████▍   | 817/1261 [16:03<08:22,  1.13s/it]
 65%|██████▍   | 818/1261 [16:04<08:21,  1.13s/it]
 65%|██████▍   | 819/1261 [16:05<08:19,  1.13s/it]
 65%|██████▌   | 820/1261 [16:06<08:21,  1.14s/it]
 65%|██████▌   | 821/1261 [16:07<08:18,  1.13s/it]
 65%|██████▌   | 822/1261 [16:08<08:18,  1.14s/it]
 65%|██████▌   | 823/1261 [16:09<08:17,  1.14s/it]
 65%|██████▌   | 824/1261 [16:11<08:15,  1.13s/it]
 65%|██████▌   | 825/1261 [16:12<08:15,  1.14s/it]
 66%|██████▌   | 826/1261 [16:13<08:18,  1.15s/it]
 66%|██████▌   | 827/1261 [16:14<08:18,  1.15s/it]
 66%|██████▌   | 828/1261 [16:15<08:21,  1.16s/it]
 66%|██████▌   | 829/1261 [16:16<08:22,  1.16s/it]
 66%|██████▌   | 830/1261 [16:18<08:22,  1.17s/it]
 66%|██████▌   | 831/1261 [16:19<08:22,  1.17s/it]
 66%|██████▌   | 832/1261 [16:20<08:20,  1.17s/it]
 66%|██████▌   | 833/1261 [16:21<08:16,  1.16s/it]
 66%|██████▌   | 834/1261 [16:22<08:13,  1.16s/it]
 66%|██████▌   | 835/1261 [16:23<08:16,  1.16s/it]
 66%|██████▋   | 836/1261 [16:25<08:17,  1.17s/it]
 66%|██████▋   | 837/1261 [16:26<08:16,  1.17s/it]
 66%|██████▋   | 838/1261 [16:27<08:14,  1.17s/it]
 67%|██████▋   | 839/1261 [16:28<08:13,  1.17s/it]
 67%|██████▋   | 840/1261 [16:29<08:10,  1.16s/it]
 67%|██████▋   | 841/1261 [16:30<08:07,  1.16s/it]
 67%|██████▋   | 842/1261 [16:32<08:04,  1.16s/it]
 67%|██████▋   | 843/1261 [16:33<08:20,  1.20s/it]
 67%|██████▋   | 844/1261 [16:34<08:24,  1.21s/it]
 67%|██████▋   | 845/1261 [16:35<08:33,  1.24s/it]
 67%|██████▋   | 846/1261 [16:37<08:48,  1.27s/it]
 67%|██████▋   | 847/1261 [16:38<08:46,  1.27s/it]
 67%|██████▋   | 848/1261 [16:39<09:02,  1.31s/it]
 67%|██████▋   | 849/1261 [16:41<09:08,  1.33s/it]
 67%|██████▋   | 850/1261 [16:42<08:53,  1.30s/it]
 67%|██████▋   | 851/1261 [16:43<08:39,  1.27s/it]
 68%|██████▊   | 852/1261 [16:44<08:39,  1.27s/it]
 68%|██████▊   | 853/1261 [16:46<09:01,  1.33s/it]
 68%|██████▊   | 854/1261 [16:47<09:00,  1.33s/it]
 68%|██████▊   | 855/1261 [16:49<08:49,  1.30s/it]
 68%|██████▊   | 856/1261 [16:50<09:16,  1.37s/it]
 68%|██████▊   | 857/1261 [16:52<09:31,  1.42s/it]
 68%|██████▊   | 858/1261 [16:53<09:43,  1.45s/it]
 68%|██████▊   | 859/1261 [16:54<09:32,  1.42s/it]
 68%|██████▊   | 860/1261 [16:56<09:11,  1.37s/it]
 68%|██████▊   | 861/1261 [16:57<08:49,  1.32s/it]
 68%|██████▊   | 862/1261 [16:58<09:10,  1.38s/it]
 68%|██████▊   | 863/1261 [17:00<09:04,  1.37s/it]
 69%|██████▊   | 864/1261 [17:01<08:50,  1.34s/it]
 69%|██████▊   | 865/1261 [17:02<08:26,  1.28s/it]
 69%|██████▊   | 866/1261 [17:03<08:11,  1.25s/it]
 69%|██████▉   | 867/1261 [17:04<07:59,  1.22s/it]
 69%|██████▉   | 868/1261 [17:06<08:01,  1.22s/it]
 69%|██████▉   | 869/1261 [17:07<07:48,  1.20s/it]
 69%|██████▉   | 870/1261 [17:08<07:54,  1.21s/it]
 69%|██████▉   | 871/1261 [17:09<08:00,  1.23s/it]
 69%|██████▉   | 872/1261 [17:11<08:05,  1.25s/it]
 69%|██████▉   | 873/1261 [17:12<08:30,  1.32s/it]
 69%|██████▉   | 874/1261 [17:14<08:33,  1.33s/it]
 69%|██████▉   | 875/1261 [17:15<08:31,  1.33s/it]
 69%|██████▉   | 876/1261 [17:16<08:32,  1.33s/it]
 70%|██████▉   | 877/1261 [17:18<08:33,  1.34s/it]
 70%|██████▉   | 878/1261 [17:19<08:35,  1.34s/it]
 70%|██████▉   | 879/1261 [17:20<08:33,  1.34s/it]
 70%|██████▉   | 880/1261 [17:22<08:36,  1.35s/it]
 70%|██████▉   | 881/1261 [17:23<08:35,  1.36s/it]
 70%|██████▉   | 882/1261 [17:24<08:27,  1.34s/it]
 70%|███████   | 883/1261 [17:25<08:06,  1.29s/it]
 70%|███████   | 884/1261 [17:27<07:51,  1.25s/it]
 70%|███████   | 885/1261 [17:28<07:48,  1.24s/it]
 70%|███████   | 886/1261 [17:29<07:45,  1.24s/it]
 70%|███████   | 887/1261 [17:30<07:40,  1.23s/it]
 70%|███████   | 888/1261 [17:31<07:29,  1.20s/it]
 70%|███████   | 889/1261 [17:33<07:20,  1.18s/it]
 71%|███████   | 890/1261 [17:34<07:15,  1.17s/it]
 71%|███████   | 891/1261 [17:35<07:11,  1.17s/it]
 71%|███████   | 892/1261 [17:36<07:08,  1.16s/it]
 71%|███████   | 893/1261 [17:37<07:03,  1.15s/it]
 71%|███████   | 894/1261 [17:38<07:03,  1.15s/it]
 71%|███████   | 895/1261 [17:39<07:01,  1.15s/it]
 71%|███████   | 896/1261 [17:41<06:59,  1.15s/it]
 71%|███████   | 897/1261 [17:42<07:07,  1.17s/it]
 71%|███████   | 898/1261 [17:43<07:03,  1.17s/it]
 71%|███████▏  | 899/1261 [17:44<07:05,  1.18s/it]
 71%|███████▏  | 900/1261 [17:45<07:18,  1.21s/it]
 71%|███████▏  | 901/1261 [17:47<07:34,  1.26s/it]
 72%|███████▏  | 902/1261 [17:48<07:44,  1.30s/it]
 72%|███████▏  | 903/1261 [17:49<07:36,  1.28s/it]
 72%|███████▏  | 904/1261 [17:51<07:48,  1.31s/it]
 72%|███████▏  | 905/1261 [17:52<07:34,  1.28s/it]
 72%|███████▏  | 906/1261 [17:53<07:23,  1.25s/it]
 72%|███████▏  | 907/1261 [17:54<07:16,  1.23s/it]
 72%|███████▏  | 908/1261 [17:56<07:04,  1.20s/it]
 72%|███████▏  | 909/1261 [17:57<07:10,  1.22s/it]
 72%|███████▏  | 910/1261 [17:58<07:02,  1.20s/it]
 72%|███████▏  | 911/1261 [17:59<06:54,  1.18s/it]
 72%|███████▏  | 912/1261 [18:00<06:50,  1.18s/it]
 72%|███████▏  | 913/1261 [18:01<06:48,  1.17s/it]
 72%|███████▏  | 914/1261 [18:03<07:06,  1.23s/it]
 73%|███████▎  | 915/1261 [18:04<07:37,  1.32s/it]
 73%|███████▎  | 916/1261 [18:06<07:37,  1.33s/it]
 73%|███████▎  | 917/1261 [18:07<07:34,  1.32s/it]
 73%|███████▎  | 918/1261 [18:08<07:32,  1.32s/it]
 73%|███████▎  | 919/1261 [18:09<07:12,  1.26s/it]
 73%|███████▎  | 920/1261 [18:11<06:57,  1.23s/it]
 73%|███████▎  | 921/1261 [18:12<06:47,  1.20s/it]
 73%|███████▎  | 922/1261 [18:13<06:43,  1.19s/it]
 73%|███████▎  | 923/1261 [18:14<06:38,  1.18s/it]
 73%|███████▎  | 924/1261 [18:15<06:34,  1.17s/it]
 73%|███████▎  | 925/1261 [18:16<06:29,  1.16s/it]
 73%|███████▎  | 926/1261 [18:18<06:34,  1.18s/it]
 74%|███████▎  | 927/1261 [18:19<06:37,  1.19s/it]
 74%|███████▎  | 928/1261 [18:20<06:28,  1.17s/it]
 74%|███████▎  | 929/1261 [18:21<06:32,  1.18s/it]
 74%|███████▍  | 930/1261 [18:22<06:35,  1.20s/it]
 74%|███████▍  | 931/1261 [18:24<06:39,  1.21s/it]
 74%|███████▍  | 932/1261 [18:25<06:47,  1.24s/it]
 74%|███████▍  | 933/1261 [18:26<06:52,  1.26s/it]
 74%|███████▍  | 934/1261 [18:27<06:49,  1.25s/it]
 74%|███████▍  | 935/1261 [18:29<06:37,  1.22s/it]
 74%|███████▍  | 936/1261 [18:30<06:33,  1.21s/it]
 74%|███████▍  | 937/1261 [18:31<06:25,  1.19s/it]
 74%|███████▍  | 938/1261 [18:32<06:19,  1.17s/it]
 74%|███████▍  | 939/1261 [18:33<06:14,  1.16s/it]
 75%|███████▍  | 940/1261 [18:34<06:11,  1.16s/it]
 75%|███████▍  | 941/1261 [18:35<06:07,  1.15s/it]
 75%|███████▍  | 942/1261 [18:37<06:07,  1.15s/it]
 75%|███████▍  | 943/1261 [18:38<06:13,  1.18s/it]
 75%|███████▍  | 944/1261 [18:39<06:13,  1.18s/it]
 75%|███████▍  | 945/1261 [18:40<06:19,  1.20s/it]
 75%|███████▌  | 946/1261 [18:41<06:19,  1.21s/it]
 75%|███████▌  | 947/1261 [18:43<06:20,  1.21s/it]
 75%|███████▌  | 948/1261 [18:44<06:26,  1.24s/it]
 75%|███████▌  | 949/1261 [18:45<06:29,  1.25s/it]
 75%|███████▌  | 950/1261 [18:46<06:20,  1.22s/it]
 75%|███████▌  | 951/1261 [18:48<06:17,  1.22s/it]
 75%|███████▌  | 952/1261 [18:49<06:10,  1.20s/it]
 76%|███████▌  | 953/1261 [18:50<06:05,  1.19s/it]
 76%|███████▌  | 954/1261 [18:51<06:01,  1.18s/it]
 76%|███████▌  | 955/1261 [18:52<05:58,  1.17s/it]
 76%|███████▌  | 956/1261 [18:53<05:54,  1.16s/it]
 76%|███████▌  | 957/1261 [18:55<05:50,  1.15s/it]
 76%|███████▌  | 958/1261 [18:56<05:49,  1.15s/it]
 76%|███████▌  | 959/1261 [18:57<05:47,  1.15s/it]
 76%|███████▌  | 960/1261 [18:58<05:46,  1.15s/it]
 76%|███████▌  | 961/1261 [18:59<05:47,  1.16s/it]
 76%|███████▋  | 962/1261 [19:00<05:47,  1.16s/it]
 76%|███████▋  | 963/1261 [19:01<05:44,  1.16s/it]
 76%|███████▋  | 964/1261 [19:03<05:44,  1.16s/it]
 77%|███████▋  | 965/1261 [19:04<05:42,  1.16s/it]
 77%|███████▋  | 966/1261 [19:05<05:41,  1.16s/it]
 77%|███████▋  | 967/1261 [19:06<05:45,  1.18s/it]
 77%|███████▋  | 968/1261 [19:07<05:41,  1.17s/it]
 77%|███████▋  | 969/1261 [19:08<05:38,  1.16s/it]
 77%|███████▋  | 970/1261 [19:10<05:35,  1.15s/it]
 77%|███████▋  | 971/1261 [19:11<05:44,  1.19s/it]
 77%|███████▋  | 972/1261 [19:12<06:08,  1.27s/it]
 77%|███████▋  | 973/1261 [19:14<06:13,  1.30s/it]
 77%|███████▋  | 974/1261 [19:15<06:15,  1.31s/it]
 77%|███████▋  | 975/1261 [19:16<06:09,  1.29s/it]
 77%|███████▋  | 976/1261 [19:17<06:00,  1.27s/it]
 77%|███████▋  | 977/1261 [19:19<05:48,  1.23s/it]
 78%|███████▊  | 978/1261 [19:20<05:42,  1.21s/it]
 78%|███████▊  | 979/1261 [19:21<05:35,  1.19s/it]
 78%|███████▊  | 980/1261 [19:22<05:33,  1.19s/it]
 78%|███████▊  | 981/1261 [19:23<05:32,  1.19s/it]
 78%|███████▊  | 982/1261 [19:24<05:27,  1.17s/it]
 78%|███████▊  | 983/1261 [19:26<05:26,  1.17s/it]
 78%|███████▊  | 984/1261 [19:27<05:22,  1.16s/it]
 78%|███████▊  | 985/1261 [19:28<05:18,  1.15s/it]
 78%|███████▊  | 986/1261 [19:29<05:16,  1.15s/it]
 78%|███████▊  | 987/1261 [19:30<05:14,  1.15s/it]
 78%|███████▊  | 988/1261 [19:31<05:11,  1.14s/it]
 78%|███████▊  | 989/1261 [19:32<05:15,  1.16s/it]
 79%|███████▊  | 990/1261 [19:34<05:18,  1.17s/it]
 79%|███████▊  | 991/1261 [19:35<05:22,  1.19s/it]
 79%|███████▊  | 992/1261 [19:36<05:31,  1.23s/it]
 79%|███████▊  | 993/1261 [19:38<05:35,  1.25s/it]
 79%|███████▉  | 994/1261 [19:39<05:29,  1.24s/it]
 79%|███████▉  | 995/1261 [19:40<05:25,  1.22s/it]
 79%|███████▉  | 996/1261 [19:41<05:19,  1.21s/it]
 79%|███████▉  | 997/1261 [19:42<05:13,  1.19s/it]
 79%|███████▉  | 998/1261 [19:43<05:07,  1.17s/it]
 79%|███████▉  | 999/1261 [19:45<05:09,  1.18s/it]
 79%|███████▉  | 1000/1261 [19:46<05:12,  1.20s/it]
 79%|███████▉  | 1001/1261 [19:47<05:06,  1.18s/it]
 79%|███████▉  | 1002/1261 [19:48<05:11,  1.20s/it]
 80%|███████▉  | 1003/1261 [19:49<05:07,  1.19s/it]
 80%|███████▉  | 1004/1261 [19:51<05:03,  1.18s/it]
 80%|███████▉  | 1005/1261 [19:52<05:00,  1.17s/it]
 80%|███████▉  | 1006/1261 [19:53<04:57,  1.17s/it]
 80%|███████▉  | 1007/1261 [19:54<04:51,  1.15s/it]
 80%|███████▉  | 1008/1261 [19:55<04:47,  1.14s/it]
 80%|████████  | 1009/1261 [19:56<04:47,  1.14s/it]
 80%|████████  | 1010/1261 [19:57<04:53,  1.17s/it]
 80%|████████  | 1011/1261 [19:59<04:52,  1.17s/it]
 80%|████████  | 1012/1261 [20:00<04:50,  1.16s/it]
 80%|████████  | 1013/1261 [20:01<04:57,  1.20s/it]
 80%|████████  | 1014/1261 [20:02<05:07,  1.25s/it]
 80%|████████  | 1015/1261 [20:04<05:10,  1.26s/it]
 81%|████████  | 1016/1261 [20:05<05:22,  1.32s/it]
 81%|████████  | 1017/1261 [20:07<05:24,  1.33s/it]
 81%|████████  | 1018/1261 [20:08<05:39,  1.40s/it]
 81%|████████  | 1019/1261 [20:10<05:43,  1.42s/it]
 81%|████████  | 1020/1261 [20:11<05:33,  1.38s/it]
 81%|████████  | 1021/1261 [20:13<06:24,  1.60s/it]
 81%|████████  | 1022/1261 [20:14<06:16,  1.58s/it]
 81%|████████  | 1023/1261 [20:16<05:58,  1.51s/it]
 81%|████████  | 1024/1261 [20:17<05:43,  1.45s/it]
 81%|████████▏ | 1025/1261 [20:18<05:23,  1.37s/it]
 81%|████████▏ | 1026/1261 [20:20<05:20,  1.36s/it]
 81%|████████▏ | 1027/1261 [20:21<05:28,  1.40s/it]
 82%|████████▏ | 1028/1261 [20:23<05:41,  1.47s/it]
 82%|████████▏ | 1029/1261 [20:24<05:52,  1.52s/it]
 82%|████████▏ | 1030/1261 [20:26<05:30,  1.43s/it]
 82%|████████▏ | 1031/1261 [20:27<05:18,  1.39s/it]
 82%|████████▏ | 1032/1261 [20:28<05:11,  1.36s/it]
 82%|████████▏ | 1033/1261 [20:29<05:01,  1.32s/it]
 82%|████████▏ | 1034/1261 [20:31<04:55,  1.30s/it]
 82%|████████▏ | 1035/1261 [20:32<04:50,  1.29s/it]
 82%|████████▏ | 1036/1261 [20:33<04:38,  1.24s/it]
 82%|████████▏ | 1037/1261 [20:35<04:50,  1.29s/it]
 82%|████████▏ | 1038/1261 [20:36<04:39,  1.25s/it]
 82%|████████▏ | 1039/1261 [20:37<04:44,  1.28s/it]
 82%|████████▏ | 1040/1261 [20:38<04:33,  1.24s/it]
 83%|████████▎ | 1041/1261 [20:39<04:28,  1.22s/it]
 83%|████████▎ | 1042/1261 [20:40<04:22,  1.20s/it]
 83%|████████▎ | 1043/1261 [20:42<04:20,  1.19s/it]
 83%|████████▎ | 1044/1261 [20:43<04:17,  1.19s/it]
 83%|████████▎ | 1045/1261 [20:44<04:13,  1.17s/it]
 83%|████████▎ | 1046/1261 [20:45<04:09,  1.16s/it]
 83%|████████▎ | 1047/1261 [20:46<04:07,  1.16s/it]
 83%|████████▎ | 1048/1261 [20:48<04:16,  1.20s/it]
 83%|████████▎ | 1049/1261 [20:49<04:12,  1.19s/it]
 83%|████████▎ | 1050/1261 [20:50<04:06,  1.17s/it]
 83%|████████▎ | 1051/1261 [20:51<04:04,  1.17s/it]
 83%|████████▎ | 1052/1261 [20:52<04:01,  1.16s/it]
 84%|████████▎ | 1053/1261 [20:53<04:06,  1.18s/it]
 84%|████████▎ | 1054/1261 [20:55<04:05,  1.19s/it]
 84%|████████▎ | 1055/1261 [20:56<04:02,  1.17s/it]
 84%|████████▎ | 1056/1261 [20:57<04:00,  1.17s/it]
 84%|████████▍ | 1057/1261 [20:58<03:58,  1.17s/it]
 84%|████████▍ | 1058/1261 [20:59<03:55,  1.16s/it]
 84%|████████▍ | 1059/1261 [21:00<03:54,  1.16s/it]
 84%|████████▍ | 1060/1261 [21:02<03:54,  1.17s/it]
 84%|████████▍ | 1061/1261 [21:03<03:54,  1.17s/it]
 84%|████████▍ | 1062/1261 [21:04<03:53,  1.17s/it]
 84%|████████▍ | 1063/1261 [21:05<03:50,  1.17s/it]
 84%|████████▍ | 1064/1261 [21:06<03:49,  1.16s/it]
 84%|████████▍ | 1065/1261 [21:07<03:46,  1.16s/it]
 85%|████████▍ | 1066/1261 [21:08<03:45,  1.15s/it]
 85%|████████▍ | 1067/1261 [21:10<03:43,  1.15s/it]
 85%|████████▍ | 1068/1261 [21:11<03:42,  1.15s/it]
 85%|████████▍ | 1069/1261 [21:12<03:48,  1.19s/it]
 85%|████████▍ | 1070/1261 [21:13<03:48,  1.20s/it]
 85%|████████▍ | 1071/1261 [21:14<03:48,  1.20s/it]
 85%|████████▌ | 1072/1261 [21:16<03:48,  1.21s/it]
 85%|████████▌ | 1073/1261 [21:17<03:43,  1.19s/it]
 85%|████████▌ | 1074/1261 [21:18<03:38,  1.17s/it]
 85%|████████▌ | 1075/1261 [21:19<03:35,  1.16s/it]
 85%|████████▌ | 1076/1261 [21:20<03:33,  1.16s/it]
 85%|████████▌ | 1077/1261 [21:22<03:41,  1.20s/it]
 85%|████████▌ | 1078/1261 [21:23<03:40,  1.20s/it]
 86%|████████▌ | 1079/1261 [21:24<03:39,  1.21s/it]
 86%|████████▌ | 1080/1261 [21:25<03:34,  1.19s/it]
 86%|████████▌ | 1081/1261 [21:26<03:37,  1.21s/it]
 86%|████████▌ | 1082/1261 [21:28<03:38,  1.22s/it]
 86%|████████▌ | 1083/1261 [21:29<03:45,  1.27s/it]
 86%|████████▌ | 1084/1261 [21:30<03:44,  1.27s/it]
 86%|████████▌ | 1085/1261 [21:32<03:48,  1.30s/it]
 86%|████████▌ | 1086/1261 [21:33<03:46,  1.29s/it]
 86%|████████▌ | 1087/1261 [21:34<03:53,  1.34s/it]
 86%|████████▋ | 1088/1261 [21:36<03:58,  1.38s/it]
 86%|████████▋ | 1089/1261 [21:37<03:47,  1.32s/it]
 86%|████████▋ | 1090/1261 [21:38<03:43,  1.31s/it]
 87%|████████▋ | 1091/1261 [21:39<03:34,  1.26s/it]
 87%|████████▋ | 1092/1261 [21:41<03:27,  1.23s/it]
 87%|████████▋ | 1093/1261 [21:42<03:35,  1.28s/it]
 87%|████████▋ | 1094/1261 [21:43<03:38,  1.31s/it]
 87%|████████▋ | 1095/1261 [21:45<03:37,  1.31s/it]
 87%|████████▋ | 1096/1261 [21:46<03:28,  1.26s/it]
 87%|████████▋ | 1097/1261 [21:47<03:22,  1.24s/it]
 87%|████████▋ | 1098/1261 [21:48<03:17,  1.21s/it]
 87%|████████▋ | 1099/1261 [21:49<03:13,  1.19s/it]
 87%|████████▋ | 1100/1261 [21:51<03:10,  1.19s/it]
 87%|████████▋ | 1101/1261 [21:52<03:08,  1.18s/it]
 87%|████████▋ | 1102/1261 [21:53<03:05,  1.17s/it]
 87%|████████▋ | 1103/1261 [21:54<03:03,  1.16s/it]
 88%|████████▊ | 1104/1261 [21:55<03:01,  1.16s/it]
 88%|████████▊ | 1105/1261 [21:56<03:00,  1.16s/it]
 88%|████████▊ | 1106/1261 [21:57<02:59,  1.16s/it]
 88%|████████▊ | 1107/1261 [21:59<02:56,  1.15s/it]
 88%|████████▊ | 1108/1261 [22:00<03:04,  1.21s/it]
 88%|████████▊ | 1109/1261 [22:01<03:00,  1.19s/it]
 88%|████████▊ | 1110/1261 [22:02<03:02,  1.21s/it]
 88%|████████▊ | 1111/1261 [22:03<02:58,  1.19s/it]
 88%|████████▊ | 1112/1261 [22:05<02:54,  1.17s/it]
 88%|████████▊ | 1113/1261 [22:06<02:57,  1.20s/it]
 88%|████████▊ | 1114/1261 [22:07<02:58,  1.22s/it]
 88%|████████▊ | 1115/1261 [22:08<02:54,  1.19s/it]
 89%|████████▊ | 1116/1261 [22:09<02:54,  1.20s/it]
 89%|████████▊ | 1117/1261 [22:11<02:50,  1.19s/it]
 89%|████████▊ | 1118/1261 [22:12<02:48,  1.18s/it]
 89%|████████▊ | 1119/1261 [22:13<02:46,  1.17s/it]
 89%|████████▉ | 1120/1261 [22:14<02:46,  1.18s/it]
 89%|████████▉ | 1121/1261 [22:15<02:48,  1.20s/it]
 89%|████████▉ | 1122/1261 [22:17<02:48,  1.21s/it]
 89%|████████▉ | 1123/1261 [22:18<02:44,  1.19s/it]
 89%|████████▉ | 1124/1261 [22:19<02:47,  1.22s/it]
 89%|████████▉ | 1125/1261 [22:20<02:44,  1.21s/it]
 89%|████████▉ | 1126/1261 [22:22<02:45,  1.23s/it]
 89%|████████▉ | 1127/1261 [22:23<02:53,  1.29s/it]
 89%|████████▉ | 1128/1261 [22:24<02:46,  1.25s/it]
 90%|████████▉ | 1129/1261 [22:25<02:40,  1.22s/it]
 90%|████████▉ | 1130/1261 [22:26<02:35,  1.19s/it]
 90%|████████▉ | 1131/1261 [22:28<02:33,  1.18s/it]
 90%|████████▉ | 1132/1261 [22:29<02:30,  1.17s/it]
 90%|████████▉ | 1133/1261 [22:30<02:28,  1.16s/it]
 90%|████████▉ | 1134/1261 [22:31<02:26,  1.15s/it]
 90%|█████████ | 1135/1261 [22:32<02:24,  1.15s/it]
 90%|█████████ | 1136/1261 [22:33<02:23,  1.15s/it]
 90%|█████████ | 1137/1261 [22:34<02:21,  1.14s/it]
 90%|█████████ | 1138/1261 [22:36<02:23,  1.17s/it]
 90%|█████████ | 1139/1261 [22:37<02:26,  1.20s/it]
 90%|█████████ | 1140/1261 [22:38<02:22,  1.18s/it]
 90%|█████████ | 1141/1261 [22:39<02:28,  1.24s/it]
 91%|█████████ | 1142/1261 [22:41<02:29,  1.25s/it]
 91%|█████████ | 1143/1261 [22:42<02:26,  1.24s/it]
 91%|█████████ | 1144/1261 [22:43<02:25,  1.25s/it]
 91%|█████████ | 1145/1261 [22:45<02:30,  1.30s/it]
 91%|█████████ | 1146/1261 [22:46<02:27,  1.29s/it]
 91%|█████████ | 1147/1261 [22:47<02:23,  1.26s/it]
 91%|█████████ | 1148/1261 [22:48<02:18,  1.23s/it]
 91%|█████████ | 1149/1261 [22:49<02:17,  1.22s/it]
 91%|█████████ | 1150/1261 [22:51<02:15,  1.22s/it]
 91%|█████████▏| 1151/1261 [22:52<02:17,  1.25s/it]
 91%|█████████▏| 1152/1261 [22:53<02:14,  1.23s/it]
 91%|█████████▏| 1153/1261 [22:54<02:09,  1.20s/it]
 92%|█████████▏| 1154/1261 [22:55<02:05,  1.18s/it]
 92%|█████████▏| 1155/1261 [22:57<02:07,  1.21s/it]
 92%|█████████▏| 1156/1261 [22:58<02:08,  1.22s/it]
 92%|█████████▏| 1157/1261 [22:59<02:11,  1.26s/it]
 92%|█████████▏| 1158/1261 [23:01<02:13,  1.29s/it]
 92%|█████████▏| 1159/1261 [23:02<02:09,  1.27s/it]
 92%|█████████▏| 1160/1261 [23:03<02:08,  1.27s/it]
 92%|█████████▏| 1161/1261 [23:04<02:03,  1.24s/it]
 92%|█████████▏| 1162/1261 [23:05<01:59,  1.21s/it]
 92%|█████████▏| 1163/1261 [23:07<01:56,  1.19s/it]
 92%|█████████▏| 1164/1261 [23:08<02:01,  1.25s/it]
 92%|█████████▏| 1165/1261 [23:09<01:56,  1.22s/it]
 92%|█████████▏| 1166/1261 [23:10<01:52,  1.19s/it]
 93%|█████████▎| 1167/1261 [23:11<01:50,  1.17s/it]
 93%|█████████▎| 1168/1261 [23:13<01:50,  1.19s/it]
 93%|█████████▎| 1169/1261 [23:14<01:49,  1.19s/it]
 93%|█████████▎| 1170/1261 [23:15<01:49,  1.20s/it]
 93%|█████████▎| 1171/1261 [23:16<01:46,  1.19s/it]
 93%|█████████▎| 1172/1261 [23:17<01:44,  1.18s/it]
 93%|█████████▎| 1173/1261 [23:18<01:42,  1.17s/it]
 93%|█████████▎| 1174/1261 [23:20<01:41,  1.17s/it]
 93%|█████████▎| 1175/1261 [23:21<01:40,  1.17s/it]
 93%|█████████▎| 1176/1261 [23:22<01:38,  1.16s/it]
 93%|█████████▎| 1177/1261 [23:23<01:37,  1.16s/it]
 93%|█████████▎| 1178/1261 [23:24<01:35,  1.16s/it]
 93%|█████████▎| 1179/1261 [23:25<01:34,  1.15s/it]
 94%|█████████▎| 1180/1261 [23:26<01:33,  1.15s/it]
 94%|█████████▎| 1181/1261 [23:28<01:32,  1.16s/it]
 94%|█████████▎| 1182/1261 [23:29<01:31,  1.16s/it]
 94%|█████████▍| 1183/1261 [23:30<01:30,  1.16s/it]
 94%|█████████▍| 1184/1261 [23:31<01:30,  1.17s/it]
 94%|█████████▍| 1185/1261 [23:32<01:29,  1.17s/it]
 94%|█████████▍| 1186/1261 [23:34<01:27,  1.16s/it]
 94%|█████████▍| 1187/1261 [23:35<01:25,  1.16s/it]
 94%|█████████▍| 1188/1261 [23:36<01:24,  1.15s/it]
 94%|█████████▍| 1189/1261 [23:37<01:22,  1.15s/it]
 94%|█████████▍| 1190/1261 [23:38<01:21,  1.15s/it]
 94%|█████████▍| 1191/1261 [23:39<01:20,  1.14s/it]
 95%|█████████▍| 1192/1261 [23:40<01:18,  1.14s/it]
 95%|█████████▍| 1193/1261 [23:42<01:17,  1.15s/it]
 95%|█████████▍| 1194/1261 [23:43<01:16,  1.15s/it]
 95%|█████████▍| 1195/1261 [23:44<01:15,  1.15s/it]
 95%|█████████▍| 1196/1261 [23:45<01:14,  1.14s/it]
 95%|█████████▍| 1197/1261 [23:46<01:12,  1.14s/it]
 95%|█████████▌| 1198/1261 [23:47<01:11,  1.14s/it]
 95%|█████████▌| 1199/1261 [23:48<01:10,  1.14s/it]
 95%|█████████▌| 1200/1261 [23:50<01:09,  1.15s/it]
 95%|█████████▌| 1201/1261 [23:51<01:08,  1.15s/it]
 95%|█████████▌| 1202/1261 [23:52<01:07,  1.14s/it]
 95%|█████████▌| 1203/1261 [23:53<01:06,  1.14s/it]
 95%|█████████▌| 1204/1261 [23:54<01:05,  1.14s/it]
 96%|█████████▌| 1205/1261 [23:55<01:03,  1.14s/it]
 96%|█████████▌| 1206/1261 [23:56<01:02,  1.14s/it]
 96%|█████████▌| 1207/1261 [23:57<01:01,  1.14s/it]
 96%|█████████▌| 1208/1261 [23:59<01:00,  1.14s/it]
 96%|█████████▌| 1209/1261 [24:00<00:59,  1.14s/it]
 96%|█████████▌| 1210/1261 [24:01<00:58,  1.14s/it]
 96%|█████████▌| 1211/1261 [24:02<00:56,  1.14s/it]
 96%|█████████▌| 1212/1261 [24:03<00:55,  1.14s/it]
 96%|█████████▌| 1213/1261 [24:04<00:54,  1.13s/it]
 96%|█████████▋| 1214/1261 [24:05<00:53,  1.14s/it]
 96%|█████████▋| 1215/1261 [24:07<00:53,  1.15s/it]
 96%|█████████▋| 1216/1261 [24:08<00:51,  1.15s/it]
 97%|█████████▋| 1217/1261 [24:09<00:50,  1.15s/it]
 97%|█████████▋| 1218/1261 [24:10<00:48,  1.14s/it]
 97%|█████████▋| 1219/1261 [24:11<00:47,  1.14s/it]
 97%|█████████▋| 1220/1261 [24:12<00:46,  1.14s/it]
 97%|█████████▋| 1221/1261 [24:13<00:45,  1.14s/it]
 97%|█████████▋| 1222/1261 [24:15<00:44,  1.14s/it]
 97%|█████████▋| 1223/1261 [24:16<00:43,  1.13s/it]
 97%|█████████▋| 1224/1261 [24:17<00:41,  1.13s/it]
 97%|█████████▋| 1225/1261 [24:18<00:40,  1.13s/it]
 97%|█████████▋| 1226/1261 [24:19<00:39,  1.13s/it]
 97%|█████████▋| 1227/1261 [24:20<00:38,  1.13s/it]
 97%|█████████▋| 1228/1261 [24:21<00:37,  1.13s/it]
 97%|█████████▋| 1229/1261 [24:22<00:36,  1.13s/it]
 98%|█████████▊| 1230/1261 [24:24<00:35,  1.14s/it]
 98%|█████████▊| 1231/1261 [24:25<00:34,  1.14s/it]
 98%|█████████▊| 1232/1261 [24:26<00:33,  1.15s/it]
 98%|█████████▊| 1233/1261 [24:27<00:32,  1.15s/it]
 98%|█████████▊| 1234/1261 [24:28<00:30,  1.14s/it]
 98%|█████████▊| 1235/1261 [24:29<00:29,  1.14s/it]
 98%|█████████▊| 1236/1261 [24:31<00:28,  1.14s/it]
 98%|█████████▊| 1237/1261 [24:32<00:27,  1.14s/it]
 98%|█████████▊| 1238/1261 [24:33<00:26,  1.14s/it]
 98%|█████████▊| 1239/1261 [24:34<00:24,  1.14s/it]
 98%|█████████▊| 1240/1261 [24:35<00:23,  1.14s/it]
 98%|█████████▊| 1241/1261 [24:36<00:22,  1.14s/it]
 98%|█████████▊| 1242/1261 [24:37<00:22,  1.17s/it]
 99%|█████████▊| 1243/1261 [24:39<00:21,  1.20s/it]
 99%|█████████▊| 1244/1261 [24:40<00:20,  1.19s/it]
 99%|█████████▊| 1245/1261 [24:41<00:19,  1.25s/it]
 99%|█████████▉| 1246/1261 [24:43<00:19,  1.27s/it]
 99%|█████████▉| 1247/1261 [24:44<00:17,  1.25s/it]
 99%|█████████▉| 1248/1261 [24:45<00:16,  1.26s/it]
 99%|█████████▉| 1249/1261 [24:46<00:15,  1.29s/it]
 99%|█████████▉| 1250/1261 [24:48<00:14,  1.30s/it]
 99%|█████████▉| 1251/1261 [24:49<00:12,  1.28s/it]
 99%|█████████▉| 1252/1261 [24:50<00:11,  1.30s/it]
 99%|█████████▉| 1253/1261 [24:52<00:10,  1.29s/it]
 99%|█████████▉| 1254/1261 [24:53<00:08,  1.26s/it]
100%|█████████▉| 1255/1261 [24:54<00:07,  1.25s/it]
100%|█████████▉| 1256/1261 [24:55<00:06,  1.26s/it]
100%|█████████▉| 1257/1261 [24:56<00:04,  1.24s/it]
100%|█████████▉| 1258/1261 [24:58<00:03,  1.22s/it]
100%|█████████▉| 1259/1261 [24:59<00:02,  1.21s/it]
100%|█████████▉| 1260/1261 [25:00<00:01,  1.25s/it]
[MoviePy] Done.
[MoviePy] >>>> Video ready: project_video_out.mp4